diff --git a/DEPS b/DEPS index 84579bba..0fc4269 100644 --- a/DEPS +++ b/DEPS
@@ -142,7 +142,7 @@ # 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': 'b42af2f2cc97441db0dd912825cd69edab5da179', + 'skia_revision': 'abb5a315af45cdfc13fbe71265e8630041f8d845', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -154,15 +154,15 @@ # 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': '4e71b2bc254677bdeac521a371402a92f6747776', + 'angle_revision': '69e46a186bb75d4e1120cd671c31e187b22741d0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '8c4c9e37bf96db64cbec5b7e26f12fc1af0b3e99', + 'swiftshader_revision': '28ae0a4b2810b9f72547382333f32e0a46d87709', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '808e9b88cf2e17fc4fe8cbfe6454e0680edd099d', + 'pdfium_revision': '5fcd7aab39b8afacd2260b4f0448a6ef7efe02fd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -481,7 +481,7 @@ }, 'src/ios/third_party/material_components_ios/src': { - 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '179cd33961527669a9a10d7450105190acaa6f24', + 'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2801e6969bfd9d3e14e7496624ca3355225148df', 'condition': 'checkout_ios', }, @@ -1191,7 +1191,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '579543531e4b15861bb2f1975e39d55aec4f7b5f', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'be75ef60754c71b59dfdce43ece95f77aaed93ab', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1359,7 +1359,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '91350f8ecf9ab2922ee062c114e4a759f24bd8d0', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '4876cb21c82f3886d67888f75bee2ab522e55a94', + Var('webrtc_git') + '/src.git' + '@' + '41300af8768cdbd9cd1785b3ecb0767ca6a43aba', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1400,7 +1400,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e46877813496a66cdc6d74a0d04f9573e52daca8', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9b7fc04d3edb6ecbb43ab899d269a90d749cecf5', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index 7aa346a..49999240 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn
@@ -1092,6 +1092,22 @@ min_sdk_version = 21 } +# Generate LocaleConfig.java so that android_webview_locale_config_java's +# compile step works. +generate_locale_config_srcjar("webview_locale_config") { + java_package = webview_locale_config_java_package +} + +# LocaleConfig.java is excluded from the generated .jar +# (via. jar_excluded_patterns) and the final version is inserted at the APK +# level - with the list of pak locales populated by looking at the assets that +# are listed in the final APK's .build_config. +android_library("android_webview_locale_config_java") { + java_files = [ "java/src/org/chromium/android_webview/AwLocaleConfig.java" ] + srcjar_deps = [ ":webview_locale_config" ] + jar_excluded_patterns = [ "*/LocaleConfig.class" ] +} + android_aidl("crash_receiver_aidl") { import_include = [ "java/src" ] sources = [
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn index cb7fe55..a130678 100644 --- a/android_webview/apk/BUILD.gn +++ b/android_webview/apk/BUILD.gn
@@ -32,7 +32,9 @@ ] deps = [ "//android_webview:android_webview_commandline_java", + "//android_webview:android_webview_locale_config_java", "//base:base_java", "//components/embedder_support/android:application_java", + "//ui/android:ui_java", ] }
diff --git a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java index 53d8dae..ffd08b64 100644 --- a/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java +++ b/android_webview/apk/java/src/com/android/webview/chromium/WebViewApkApplication.java
@@ -7,12 +7,14 @@ import android.app.Application; import android.content.Context; +import org.chromium.android_webview.AwLocaleConfig; import org.chromium.android_webview.command_line.CommandLineUtil; import org.chromium.base.ContextUtils; import org.chromium.base.PathUtils; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.components.embedder_support.application.FontPreloadingWorkaround; +import org.chromium.ui.base.ResourceBundle; /** * Application subclass for SystemWebView and Trichrome. @@ -30,7 +32,8 @@ protected void attachBaseContext(Context context) { super.attachBaseContext(context); ContextUtils.initApplicationContext(this); - maybeInitProcessGlobals(); + ResourceBundle.setAvailablePakLocales( + new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales()); } @Override
diff --git a/android_webview/browser/aw_contents_io_thread_client.cc b/android_webview/browser/aw_contents_io_thread_client.cc index 89a1785..7a472d02 100644 --- a/android_webview/browser/aw_contents_io_thread_client.cc +++ b/android_webview/browser/aw_contents_io_thread_client.cc
@@ -8,6 +8,7 @@ #include <memory> #include <utility> +#include "android_webview/browser/input_stream.h" #include "android_webview/browser/net/aw_web_resource_request.h" #include "android_webview/browser/net/aw_web_resource_response.h" #include "android_webview/common/devtools_instrumentation.h" @@ -19,6 +20,7 @@ #include "base/bind.h" #include "base/containers/flat_set.h" #include "base/lazy_instance.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/synchronization/lock.h" #include "base/threading/scoped_blocking_call.h" @@ -358,7 +360,7 @@ }; // Record UMA whether the request was intercepted and if so what kind of scheme. -void RecordInterceptedType(bool response_is_null, const std::string& url) { +void RecordInterceptedScheme(bool response_is_null, const std::string& url) { InterceptionType type = InterceptionType::kNoIntercept; if (!response_is_null) { GURL gurl(url); @@ -378,6 +380,37 @@ "Android.WebView.ShouldInterceptRequest.InterceptionType", type); } +// Record UMA for the custom response status code for the intercepted requests +// where input stream is null. UMA is recorded only when the status codes and +// reason phrases are actually valid. +void RecordResponseStatusCode(JNIEnv* env, AwWebResourceResponse* response) { + DCHECK(response); + DCHECK(!response->HasInputStream(env)); + + int status_code; + std::string reason_phrase; + bool status_info_valid = + response->GetStatusInfo(env, &status_code, &reason_phrase); + + if (!status_info_valid) { + // Status code is not necessary set properly in the response, + // e.g. Webview's WebResourceResponse(String, String, InputStream) [*] + // does not actually set the status code or the reason phrase. In this case + // we just record a zero status code. + // The other constructor (long version) or the #setStatusCodeAndReasonPhrase + // method does actually perform validity checks on status code and reason + // phrase arguments. + // [*] + // https://developer.android.com/reference/android/webkit/WebResourceResponse.html + status_code = 0; + } + + base::UmaHistogramSparse( + "Android.WebView.ShouldInterceptRequest.NullInputStream." + "ResponseStatusCode", + status_code); +} + std::unique_ptr<AwWebResourceResponse> RunShouldInterceptRequest( AwWebResourceRequest request, JavaObjectWeakGlobalRef ref) { @@ -401,10 +434,19 @@ java_web_resource_request.jheader_names, java_web_resource_request.jheader_values); - RecordInterceptedType(ret.is_null(), request.url); + RecordInterceptedScheme(ret.is_null(), request.url); - return std::unique_ptr<AwWebResourceResponse>( - ret.is_null() ? nullptr : new AwWebResourceResponse(ret)); + if (ret.is_null()) + return std::unique_ptr<AwWebResourceResponse>(nullptr); + + AwWebResourceResponse* response = new AwWebResourceResponse(ret); + if (!response->HasInputStream(env)) { + // Only record UMA for cases where the input stream is null (see + // crbug.com/974273). + RecordResponseStatusCode(env, response); + } + + return std::unique_ptr<AwWebResourceResponse>(response); } std::unique_ptr<AwWebResourceResponse> ReturnNull() {
diff --git a/android_webview/browser/net/aw_web_resource_response.cc b/android_webview/browser/net/aw_web_resource_response.cc index 82c6021..1576c8f 100644 --- a/android_webview/browser/net/aw_web_resource_response.cc +++ b/android_webview/browser/net/aw_web_resource_response.cc
@@ -26,6 +26,12 @@ AwWebResourceResponse::~AwWebResourceResponse() {} +bool AwWebResourceResponse::HasInputStream(JNIEnv* env) const { + ScopedJavaLocalRef<jobject> jstream = + Java_AwWebResourceResponse_getData(env, java_object_); + return !jstream.is_null(); +} + std::unique_ptr<InputStream> AwWebResourceResponse::GetInputStream( JNIEnv* env) const { ScopedJavaLocalRef<jobject> jstream =
diff --git a/android_webview/browser/net/aw_web_resource_response.h b/android_webview/browser/net/aw_web_resource_response.h index e7038ba..307360cb 100644 --- a/android_webview/browser/net/aw_web_resource_response.h +++ b/android_webview/browser/net/aw_web_resource_response.h
@@ -31,6 +31,7 @@ AwWebResourceResponse(const base::android::JavaRef<jobject>& obj); ~AwWebResourceResponse(); + bool HasInputStream(JNIEnv* env) const; std::unique_ptr<InputStream> GetInputStream(JNIEnv* env) const; bool GetMimeType(JNIEnv* env, std::string* mime_type) const; bool GetCharset(JNIEnv* env, std::string* charset) const;
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn index b57656c8..d18d164 100644 --- a/android_webview/glue/BUILD.gn +++ b/android_webview/glue/BUILD.gn
@@ -11,6 +11,7 @@ deps = [ "//android_webview:android_webview_commandline_java", "//android_webview:android_webview_java", + "//android_webview:android_webview_locale_config_java", "//android_webview:android_webview_platform_services_java", "//android_webview:system_webview_manifest", "//android_webview/support_library/boundary_interfaces:boundary_interface_java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java index acca205..84c4eae 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -25,6 +25,7 @@ import org.chromium.android_webview.AwContentsStatics; import org.chromium.android_webview.AwCookieManager; import org.chromium.android_webview.AwFirebaseConfig; +import org.chromium.android_webview.AwLocaleConfig; import org.chromium.android_webview.AwNetworkChangeNotifierRegistrationPolicy; import org.chromium.android_webview.AwProxyController; import org.chromium.android_webview.AwQuotaManagerBridge; @@ -54,6 +55,7 @@ import org.chromium.base.task.TaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.net.NetworkChangeNotifier; +import org.chromium.ui.base.ResourceBundle; /** * Class controlling the Chromium initialization for WebView. @@ -141,6 +143,9 @@ JNIUtils.setClassLoader(WebViewChromiumAwInit.class.getClassLoader()); + ResourceBundle.setAvailablePakLocales( + new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales()); + // We are rewriting Java resources in the background. // NOTE: Any reference to Java resources will cause a crash.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java b/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java new file mode 100644 index 0000000..a383ef0 --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/AwLocaleConfig.java
@@ -0,0 +1,17 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview; + +/** + * Simple class that provides access to the array of uncompressed pak locales. See + * //android_webview/BUILD.gn for more details. + */ +public final class AwLocaleConfig { + private AwLocaleConfig() {} + + public static String[] getWebViewSupportedPakLocales() { + return LocaleConfig.UNCOMPRESSED_LOCALES; + } +}
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni index b1a4267..8838a5c 100644 --- a/android_webview/system_webview_apk_tmpl.gni +++ b/android_webview/system_webview_apk_tmpl.gni
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//android_webview/variables.gni") import("//base/android/proguard/proguard.gni") import("//build/config/android/config.gni") import("//build/config/android/rules.gni") @@ -33,6 +34,7 @@ ] target_sdk_version = android_sdk_version + locale_config_java_packages = [ webview_locale_config_java_package ] if (!defined(alternative_android_sdk_dep)) { alternative_android_sdk_dep = webview_framework_dep
diff --git a/android_webview/test/BUILD.gn b/android_webview/test/BUILD.gn index c7899db9..31ef7f2 100644 --- a/android_webview/test/BUILD.gn +++ b/android_webview/test/BUILD.gn
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//android_webview/variables.gni") import("//build/config/android/rules.gni") import("//device/vr/buildflags/buildflags.gni") import("//testing/test.gni") @@ -46,6 +47,7 @@ ":android_webview_apk_assets", ":android_webview_apk_resources", "//android_webview:android_webview_java", + "//android_webview:android_webview_locale_config_java", "//android_webview:locale_pak_assets", "//android_webview:platform_service_bridge_upstream_implementation_java", "//android_webview/apk:apk_java", @@ -75,6 +77,7 @@ "shell/src/org/chromium/android_webview/test/OnlyRunIn.java", "shell/src/org/chromium/android_webview/test/TestContentProvider.java", ] + locale_config_java_packages = [ webview_locale_config_java_package ] shared_libraries = [ ":libstandalonelibwebviewchromium" ]
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java index 9bb1f78..aff2047 100644 --- a/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java +++ b/android_webview/test/shell/src/org/chromium/android_webview/shell/AwShellApplication.java
@@ -7,9 +7,11 @@ import android.app.Application; import android.content.Context; +import org.chromium.android_webview.AwLocaleConfig; import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.PathUtils; +import org.chromium.ui.base.ResourceBundle; /** * The android_webview shell Application subclass. @@ -23,5 +25,7 @@ ContextUtils.initApplicationContext(this); PathUtils.setPrivateDataDirectorySuffix("webview"); CommandLine.initFromFile("/data/local/tmp/android-webview-command-line"); + ResourceBundle.setAvailablePakLocales( + new String[] {}, AwLocaleConfig.getWebViewSupportedPakLocales()); } }
diff --git a/android_webview/variables.gni b/android_webview/variables.gni index 4e0b03b..070e5e9 100644 --- a/android_webview/variables.gni +++ b/android_webview/variables.gni
@@ -11,3 +11,5 @@ "//android_webview:platform_service_bridge_upstream_implementation_java", "//android_webview:system_webview_resources", ] + +webview_locale_config_java_package = "org.chromium.android_webview"
diff --git a/ash/accelerators/debug_commands.cc b/ash/accelerators/debug_commands.cc index d3fbb02..d966f7f8 100644 --- a/ash/accelerators/debug_commands.cc +++ b/ash/accelerators/debug_commands.cc
@@ -4,9 +4,13 @@ #include "ash/accelerators/debug_commands.h" +#include <string> +#include <utility> + #include "ash/accelerators/accelerator_commands.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/toast_data.h" +#include "ash/public/cpp/window_properties.h" #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/system/toast/toast_manager_impl.h" @@ -23,6 +27,7 @@ #include "services/service_manager/public/cpp/connector.h" #include "ui/accessibility/ax_tree_id.h" #include "ui/accessibility/platform/aura_window_properties.h" +#include "ui/aura/client/aura_constants.h" #include "ui/compositor/debug_utils.h" #include "ui/compositor/layer.h" #include "ui/display/manager/display_manager.h" @@ -85,6 +90,14 @@ std::string* tree_id = window->GetProperty(ui::kChildAXTreeID); if (tree_id) *out << " ax_tree_id=" << *tree_id; + base::string16 title(window->GetTitle()); + if (!title.empty()) + *out << " title=" << title; + int app_type = window->GetProperty(aura::client::kAppType); + *out << " app_type=" << app_type; + std::string* pkg_name = window->GetProperty(ash::kArcPackageNameKey); + if (pkg_name) + *out << " pkg_name=" << *pkg_name; *out << '\n'; for (aura::Window* child : window->children())
diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc index 980360e..40f9ff0 100644 --- a/ash/autoclick/autoclick_controller.cc +++ b/ash/autoclick/autoclick_controller.cc
@@ -265,9 +265,10 @@ void AutoclickController::OnAutoclickScrollableBoundsFound( gfx::Rect& bounds_in_screen) { - // TODO(katie): Don't call this on the very first time scrollable bounds - // are found for each time the type is changed to scroll. We want the - // default first position of the scrollbar to be next to the menu bubble. + // The very first time scrollable bounds are found, the default first + // position of the scrollbar to be next to the menu bubble. + if (is_initial_scroll_location_) + return; menu_bubble_controller_->SetScrollPosition(bounds_in_screen, scroll_location_); } @@ -341,6 +342,7 @@ mouse_event_flags_); } else { scroll_location_ = gesture_anchor_location_; + is_initial_scroll_location_ = false; UpdateScrollPosition(scroll_location_); Shell::Get() ->accessibility_controller() @@ -498,13 +500,12 @@ } void AutoclickController::InitializeScrollLocation() { - // TODO(katie): Set the first scroll location to the center of the active - // window or view, which will be found using the automation API. Currently - // just setting it to the center of the root window. + // Sets the scroll location to the center of the root window. scroll_location_ = ash::Shell::Get() ->GetPrimaryRootWindow() ->GetBoundsInScreen() .CenterPoint(); + is_initial_scroll_location_ = true; Shell::Get() ->accessibility_controller() ->RequestAutoclickScrollableBoundsForPoint(scroll_location_);
diff --git a/ash/autoclick/autoclick_controller.h b/ash/autoclick/autoclick_controller.h index dfe4312..6c5a369 100644 --- a/ash/autoclick/autoclick_controller.h +++ b/ash/autoclick/autoclick_controller.h
@@ -189,6 +189,10 @@ // The point at which the next scroll event will occur. gfx::Point scroll_location_{-kDefaultAutoclickMovementThreshold, -kDefaultAutoclickMovementThreshold}; + // Whether the current scroll_location_ is the initial one set automatically, + // or if false, it was chosen explicitly by the user. The scroll bubble + // positions are different in these two cases. + bool is_initial_scroll_location_ = true; // Whether the cursor is currently over a scroll button. If true, new gestures // will not be started. This ensures the autoclick ring is not drawn over // the scroll position buttons, and extra clicks will not be generated there.
diff --git a/ash/autoclick/autoclick_unittest.cc b/ash/autoclick/autoclick_unittest.cc index aa12c73..032b2d3 100644 --- a/ash/autoclick/autoclick_unittest.cc +++ b/ash/autoclick/autoclick_unittest.cc
@@ -35,6 +35,10 @@ namespace ash { +namespace { +const int kScrollToMenuBoundsBuffer = 18; +} + class MouseEventCapturer : public ui::EventHandler { public: MouseEventCapturer() { Reset(); } @@ -1309,4 +1313,67 @@ EnableExperimentalAutoclickFlag(false); } +TEST_F(AutoclickTest, ScrollMenuBubblePostioning) { + UpdateDisplay("800x600"); + EnableExperimentalAutoclickFlag(true); + GetAutoclickController()->SetEnabled(true, false /* do not show dialog */); + + Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( + AutoclickMenuPosition::kBottomRight); + GetAutoclickController()->SetAutoclickEventType(AutoclickEventType::kScroll); + + ASSERT_TRUE(GetAutoclickScrollView()); + + // Set the bounds to be the entire window. + gfx::Rect display_bounds = gfx::Rect(0, 0, 800, 600); + GetAutoclickController()->OnAutoclickScrollableBoundsFound(display_bounds); + + // The scroll bubble should start near the autoclick menu. + gfx::Rect scroll_bounds = GetAutoclickScrollView()->GetBoundsInScreen(); + gfx::Rect menu_bounds = GetAutoclickMenuView()->GetBoundsInScreen(); + EXPECT_LT(menu_bounds.ManhattanInternalDistance(scroll_bounds), + kScrollToMenuBoundsBuffer); + + // Moving the autoclick menu around the screen moves the scroll bubble too. + Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( + AutoclickMenuPosition::kBottomLeft); + scroll_bounds = GetAutoclickScrollView()->GetBoundsInScreen(); + menu_bounds = GetAutoclickMenuView()->GetBoundsInScreen(); + EXPECT_LT(menu_bounds.ManhattanInternalDistance(scroll_bounds), + kScrollToMenuBoundsBuffer); + + Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( + AutoclickMenuPosition::kTopLeft); + scroll_bounds = GetAutoclickScrollView()->GetBoundsInScreen(); + menu_bounds = GetAutoclickMenuView()->GetBoundsInScreen(); + EXPECT_LT(menu_bounds.ManhattanInternalDistance(scroll_bounds), + kScrollToMenuBoundsBuffer); + + Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( + AutoclickMenuPosition::kTopRight); + scroll_bounds = GetAutoclickScrollView()->GetBoundsInScreen(); + menu_bounds = GetAutoclickMenuView()->GetBoundsInScreen(); + EXPECT_LT(menu_bounds.ManhattanInternalDistance(scroll_bounds), + kScrollToMenuBoundsBuffer); + + // However, if we dwell somewhere else, the autoclick scroll menu will now + // move out of the corner and near that point when the display bounds are + // found. + gfx::Point scroll_point = gfx::Point(0, 0); + GetEventGenerator()->MoveMouseTo(scroll_point); + base::RunLoop().RunUntilIdle(); + GetAutoclickController()->OnAutoclickScrollableBoundsFound(display_bounds); + scroll_bounds = GetAutoclickScrollView()->GetBoundsInScreen(); + EXPECT_GT(menu_bounds.ManhattanInternalDistance(scroll_bounds), + kScrollToMenuBoundsBuffer); + + // Moving the bubble menu now does not change the scroll bubble's position, + // it remains near its point. + Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition( + AutoclickMenuPosition::kBottomRight); + EXPECT_EQ(GetAutoclickScrollView()->GetBoundsInScreen(), scroll_bounds); + + EnableExperimentalAutoclickFlag(false); +} + } // namespace ash
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc index 4bff6bf..6bca03d 100644 --- a/ash/highlighter/highlighter_controller.cc +++ b/ash/highlighter/highlighter_controller.cc
@@ -10,7 +10,6 @@ #include "ash/highlighter/highlighter_gesture_util.h" #include "ash/highlighter/highlighter_result_view.h" #include "ash/highlighter/highlighter_view.h" -#include "ash/public/cpp/scale_utility.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/shell.h" #include "ash/shell_state.h" @@ -43,20 +42,6 @@ box.width() + pen_tip_size.width(), pen_tip_size.height()); } -// This method computes the scale required to convert window-relative DIP -// coordinates to the coordinate space of the screenshot taken from that window. -// The transform returned by WindowTreeHost::GetRootTransform translates points -// from DIP to physical screen pixels (by taking into account not only the -// scale but also the rotation and the offset). -// However, the screenshot bitmap is always oriented the same way as the window -// from which it was taken, and has zero offset. -// The code below deduces the scale from the transform by applying it to a pair -// of points separated by the distance of 1, and measuring the distance between -// the transformed points. -float GetScreenshotScale(aura::Window* window) { - return GetScaleFactorForTransform(window->GetHost()->GetRootTransform()); -} - } // namespace HighlighterController::HighlighterController() : weak_factory_(this) { @@ -209,17 +194,7 @@ Shell::Get()->shell_state()->SetRootWindowForNewWindows( current_window->GetRootWindow()); - // TODO(muyuanli): Delete the check when native assistant is default on. - // This is a temporary workaround to support both ARC-based assistant - // and native assistant. In ARC-based assistant, we send the rect in pixels - // to ARC side, where the app will crop the screenshot. In native assistant, - // we pass the rect directly to UI snapshot API, which assumes coordinates - // in DP. - const gfx::Rect selection_rect = - chromeos::switches::IsAssistantEnabled() - ? gfx::ToEnclosingRect(box) - : gfx::ToEnclosingRect( - gfx::ScaleRect(box, GetScreenshotScale(current_window))); + const gfx::Rect selection_rect = gfx::ToEnclosingRect(box); for (auto& observer : observers_) observer.OnHighlighterSelectionRecognized(selection_rect);
diff --git a/ash/highlighter/highlighter_controller_unittest.cc b/ash/highlighter/highlighter_controller_unittest.cc index ccecc4f..3f23519 100644 --- a/ash/highlighter/highlighter_controller_unittest.cc +++ b/ash/highlighter/highlighter_controller_unittest.cc
@@ -294,17 +294,12 @@ controller_->RemoveObserver(&observer); } -// Disabled due to https://crbug.com/917113. -TEST_F(HighlighterControllerTest, DISABLED_HighlighterGesturesScaled) { +TEST_F(HighlighterControllerTest, HighlighterGesturesScaled) { controller_test_api_->SetEnabled(true); ui::test::EventGenerator* event_generator = GetEventGenerator(); event_generator->EnterPenPointerMode(); - const gfx::Rect original_rect(200, 100, 400, 300); - - // Allow for rounding errors. - gfx::Rect inflated(original_rect); - inflated.Inset(-1, -1); + const gfx::Rect original_px(200, 100, 400, 300); constexpr float display_scales[] = {1.f, 1.5f, 2.0f}; constexpr float ui_scales[] = {0.5f, 0.67f, 1.0f, 1.25f, @@ -321,12 +316,21 @@ UpdateDisplayAndWaitForCompositingEnded(display_spec); controller_test_api_->ResetSelection(); - TraceRect(original_rect); + TraceRect(original_px); EXPECT_TRUE(controller_test_api_->HandleSelectionCalled()); - const gfx::Rect selection = controller_test_api_->selection(); - EXPECT_TRUE(inflated.Contains(selection)); - EXPECT_TRUE(selection.Contains(original_rect)); + const float combined_scale = display_scale * ui_scale; + + const gfx::Rect selection_dp = controller_test_api_->selection(); + const gfx::Rect selection_px = gfx::ToEnclosingRect( + gfx::ScaleRect(gfx::RectF(selection_dp), combined_scale)); + EXPECT_TRUE(selection_px.Contains(original_px)); + + gfx::Rect inflated_px(original_px); + // Allow for rounding errors within 1dp. + const int error_margin = static_cast<int>(std::ceil(combined_scale)); + inflated_px.Inset(-error_margin, -error_margin); + EXPECT_TRUE(inflated_px.Contains(selection_px)); } } } @@ -414,9 +418,7 @@ } // Test that the selection is never crossing the screen bounds. -// -// Disabled due to https://crbug.com/917113. -TEST_F(HighlighterControllerTest, DISABLED_SelectionInsideScreen) { +TEST_F(HighlighterControllerTest, SelectionInsideScreen) { controller_test_api_->SetEnabled(true); ui::test::EventGenerator* event_generator = GetEventGenerator(); event_generator->EnterPenPointerMode();
diff --git a/base/android/java/src/org/chromium/base/LocaleUtils.java b/base/android/java/src/org/chromium/base/LocaleUtils.java index 10d1142..a8f07e83 100644 --- a/base/android/java/src/org/chromium/base/LocaleUtils.java +++ b/base/android/java/src/org/chromium/base/LocaleUtils.java
@@ -184,16 +184,6 @@ return languageTag.substring(0, pos); } - /** @return true if the language is supported by Chrome. */ - public static boolean isLanguageSupported(String language) { - for (String languageTag : BuildConfig.COMPRESSED_LOCALES) { - if (toLanguage(languageTag).equals(language)) { - return true; - } - } - return false; - } - /** * @return a language tag string that represents the default locale. * The language tag is well-formed IETF BCP 47 language tag with language and country
diff --git a/base/android/java/templates/BuildConfig.template b/base/android/java/templates/BuildConfig.template index 2dcf476d..32bddbc 100644 --- a/base/android/java/templates/BuildConfig.template +++ b/base/android/java/templates/BuildConfig.template
@@ -46,22 +46,6 @@ public static MAYBE_FINAL boolean IS_CHROME_BRANDED MAYBE_FALSE; #endif - // Sorted list of locales that have a compressed .pak within assets. - // Stored as an array because AssetManager.list() is slow. -#if defined(COMPRESSED_LOCALE_LIST) - public static MAYBE_FINAL String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST; -#else - public static MAYBE_FINAL String[] COMPRESSED_LOCALES = {}; -#endif - - // Sorted list of locales that have an uncompressed .pak within assets. - // Stored as an array because AssetManager.list() is slow. -#if defined(UNCOMPRESSED_LOCALE_LIST) - public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST; -#else - public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = {}; -#endif - // The ID of the android string resource that stores the product version. // This layer of indirection is necessary to make the resource dependency // optional for android_apk targets/base_java (ex. for cronet).
diff --git a/base/android/orderfile/OWNERS b/base/android/orderfile/OWNERS index d45b803a..3301555 100644 --- a/base/android/orderfile/OWNERS +++ b/base/android/orderfile/OWNERS
@@ -1,3 +1,2 @@ lizeb@chromium.org -mattcary@chromium.org pasko@chromium.org
diff --git a/base/task/thread_pool/task_tracker.cc b/base/task/thread_pool/task_tracker.cc index 35a2e16..5fa1d6b6 100644 --- a/base/task/thread_pool/task_tracker.cc +++ b/base/task/thread_pool/task_tracker.cc
@@ -341,9 +341,7 @@ } } -// TODO(gab): Figure out why TS_UNCHECKED_READ is insufficient to make thread -// analysis of |shutdown_event_| happy on POSIX. -void TaskTracker::CompleteShutdown() NO_THREAD_SAFETY_ANALYSIS { +void TaskTracker::CompleteShutdown() { // It is safe to access |shutdown_event_| without holding |lock_| because the // pointer never changes after being set by StartShutdown(), which must be // called before this.
diff --git a/base/test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java b/base/test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java index 1bd869c..eba0f61a 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java +++ b/base/test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java
@@ -9,10 +9,10 @@ import org.json.JSONArray; import org.json.JSONObject; import org.junit.runner.Description; +import org.junit.runner.notification.Failure; import org.chromium.base.Log; -import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -38,6 +38,14 @@ Arrays.asList(new String[] {"toString", "hashCode", "annotationType", "equals"})); private final Map<Class<?>, JSONObject> mTestClassJsonMap = new HashMap<>(); + private Failure mFirstFailure; + + @Override + public void testFailure(Failure failure) { + if (mFirstFailure == null) { + mFirstFailure = failure; + } + } /** * Store the test method description to a Map at the beginning of a test run. @@ -62,23 +70,17 @@ * Create a JSONArray with all the test class JSONObjects and save it to listed output path. */ public void saveTestsToJson(String outputPath) throws IOException { - Writer writer = null; - File file = new File(outputPath); - try { - writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); + if (mFirstFailure != null) { + throw new RuntimeException( + "Failed on " + mFirstFailure.getDescription(), mFirstFailure.getException()); + } + + try (Writer writer = new OutputStreamWriter(new FileOutputStream(outputPath), "UTF-8")) { JSONArray allTestClassesJSON = new JSONArray(mTestClassJsonMap.values()); writer.write(allTestClassesJSON.toString()); } catch (IOException e) { Log.e(TAG, "failed to write json to file", e); throw e; - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - // Intentionally ignore IOException when closing writer - } - } } }
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc index 4a03e612..b15eafc 100644 --- a/base/test/scoped_task_environment.cc +++ b/base/test/scoped_task_environment.cc
@@ -217,8 +217,9 @@ if (!run_time) return base::nullopt; - // Check if we have a task that should be running now. - if (run_time <= now_ticks_) + // Check if we have a task that should be running now. Reading |now_ticks_| + // from the main thread doesn't require the lock. + if (run_time <= TS_UNCHECKED_READ(now_ticks_)) return base::TimeDelta(); // The next task is a future delayed task. Since we're using mock time, we @@ -293,7 +294,8 @@ // Only ever written to from the main sequence. Start from real Now() instead // of zero to give a more realistic view to tests. - TimeTicks now_ticks_{base::subtle::TimeTicksNowIgnoringOverride()}; + TimeTicks now_ticks_ GUARDED_BY(now_ticks_lock_){ + base::subtle::TimeTicksNowIgnoringOverride()}; }; ScopedTaskEnvironment::MockTimeDomain*
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py index 9c551b9..7c78255 100755 --- a/build/android/gyp/dex.py +++ b/build/android/gyp/dex.py
@@ -4,9 +4,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import json +import argparse import logging -import optparse import os import re import shutil @@ -21,76 +20,55 @@ import convert_dex_profile -def _CheckFilePathEndsWithJar(parser, file_path): - if not file_path.endswith(".jar"): - parser.error("%s does not end in .jar" % file_path) - - -def _CheckFilePathsEndWithJar(parser, file_paths): - for file_path in file_paths: - _CheckFilePathEndsWithJar(parser, file_path) +_FIXED_ZIP_HEADER_LEN = 30 def _ParseArgs(args): args = build_utils.ExpandFileArgs(args) + parser = argparse.ArgumentParser() - parser = optparse.OptionParser() build_utils.AddDepfileOption(parser) + parser.add_argument('--output', required=True, help='Dex output path.') + parser.add_argument('--input-list', help='GN-list of additional input paths.') + parser.add_argument( + '--main-dex-list-path', + help='File containing a list of the classes to include in the main dex.') + parser.add_argument( + '--multi-dex', + action='store_true', + help='Allow multiple dex files within output.') + parser.add_argument('--d8-jar-path', required=True, help='Path to D8 jar.') + parser.add_argument( + '--release', + action='store_true', + help='Run D8 in release mode. Release mode maximises main dex and ' + 'deletes non-essential line number information (vs debug which minimizes ' + 'main dex and keeps all line number information, and then some.') + parser.add_argument( + '--min-api', help='Minimum Android API level compatibility.') + parser.add_argument('inputs', nargs='*', help='Input .jar files.') - parser.add_option('--output-directory', - default=os.getcwd(), - help='Path to the output build directory.') - parser.add_option('--dex-path', help='Dex output path.') - parser.add_option('--configuration-name', - help='The build CONFIGURATION_NAME.') - parser.add_option('--proguard-enabled', - help='"true" if proguard is enabled.') - parser.add_option('--debug-build-proguard-enabled', - help='"true" if proguard is enabled for debug build.') - parser.add_option('--proguard-enabled-input-path', - help=('Path to dex in Release mode when proguard ' - 'is enabled.')) - parser.add_option('--inputs', help='A list of additional input paths.') - parser.add_option('--excluded-paths', - help='A list of paths to exclude from the dex file.') - parser.add_option('--main-dex-list-path', - help='A file containing a list of the classes to ' - 'include in the main dex.') - parser.add_option('--multidex-configuration-path', - help='A JSON file containing multidex build configuration.') - parser.add_option('--multi-dex', default=False, action='store_true', - help='Generate multiple dex files.') - parser.add_option('--d8-jar-path', help='Path to D8 jar.') - parser.add_option('--release', action='store_true', default=False, - help='Run D8 in release mode. Release mode maximises main ' - 'dex and deletes non-essential line number information ' - '(vs debug which minimizes main dex and keeps all line ' - 'number information, and then some.') - parser.add_option('--min-api', - help='Minimum Android API level compatibility.') - - parser.add_option('--dexlayout-profile', - help=('Text profile for dexlayout. If present, a dexlayout ' - 'pass will happen')) - parser.add_option('--profman-path', - help=('Path to ART profman binary. There should be a ' - 'lib/ directory at the same path containing shared ' - 'libraries (shared with dexlayout).')) - parser.add_option('--dexlayout-path', - help=('Path to ART dexlayout binary. There should be a ' - 'lib/ directory at the same path containing shared ' - 'libraries (shared with dexlayout).')) - parser.add_option('--dexdump-path', help='Path to dexdump binary.') - parser.add_option( + group = parser.add_argument_group('Dexlayout') + group.add_argument( + '--dexlayout-profile', + help=('Text profile for dexlayout. If present, a dexlayout ' + 'pass will happen')) + group.add_argument( + '--profman-path', + help=('Path to ART profman binary. There should be a lib/ directory at ' + 'the same path with shared libraries (shared with dexlayout).')) + group.add_argument( + '--dexlayout-path', + help=('Path to ART dexlayout binary. There should be a lib/ directory at ' + 'the same path with shared libraries (shared with dexlayout).')) + group.add_argument('--dexdump-path', help='Path to dexdump binary.') + group.add_argument( '--proguard-mapping-path', help=('Path to proguard map from obfuscated symbols in the jar to ' - 'unobfuscated symbols present in the code. If not ' - 'present, the jar is assumed not to be obfuscated.')) + 'unobfuscated symbols present in the code. If not present, the jar ' + 'is assumed not to be obfuscated.')) - options, paths = parser.parse_args(args) - - required_options = ('d8_jar_path',) - build_utils.CheckOptions(options, parser, required=required_options) + options = parser.parse_args(args) if options.dexlayout_profile: build_utils.CheckOptions( @@ -98,67 +76,19 @@ parser, required=('profman_path', 'dexlayout_path', 'dexdump_path')) elif options.proguard_mapping_path is not None: - raise Exception('Unexpected proguard mapping without dexlayout') - - if options.multidex_configuration_path: - with open(options.multidex_configuration_path) as multidex_config_file: - multidex_config = json.loads(multidex_config_file.read()) - options.multi_dex = multidex_config.get('enabled', False) + parser.error('Unexpected proguard mapping without dexlayout') if options.main_dex_list_path and not options.multi_dex: - logging.warning('--main-dex-list-path is unused if multidex is not enabled') + parser.error('--main-dex-list-path is unused if multidex is not enabled') - if options.inputs: - options.inputs = build_utils.ParseGnList(options.inputs) - _CheckFilePathsEndWithJar(parser, options.inputs) - if options.excluded_paths: - options.excluded_paths = build_utils.ParseGnList(options.excluded_paths) + if options.input_list: + options.inputs += build_utils.ParseGnList(options.input_list) - if options.proguard_enabled_input_path: - _CheckFilePathEndsWithJar(parser, options.proguard_enabled_input_path) - _CheckFilePathsEndWithJar(parser, paths) - - return options, paths - - -def _MoveTempDexFile(tmp_dex_dir, dex_path): - """Move the temp dex file out of |tmp_dex_dir|. - - Args: - tmp_dex_dir: Path to temporary directory created with tempfile.mkdtemp(). - The directory should have just a single file. - dex_path: Target path to move dex file to. - - Raises: - Exception if there are multiple files in |tmp_dex_dir|. - """ - tempfiles = os.listdir(tmp_dex_dir) - if len(tempfiles) > 1: - raise Exception('%d files created, expected 1' % len(tempfiles)) - - tmp_dex_path = os.path.join(tmp_dex_dir, tempfiles[0]) - shutil.move(tmp_dex_path, dex_path) - - -def _NoClassFiles(jar_paths): - """Returns True if there are no .class files in the given JARs. - - Args: - jar_paths: list of strings representing JAR file paths. - - Returns: - (bool) True if no .class files are found. - """ - for jar_path in jar_paths: - with zipfile.ZipFile(jar_path) as jar: - if any(name.endswith('.class') for name in jar.namelist()): - return False - return True + return options def _RunD8(dex_cmd, input_paths, output_path): - dex_cmd += ['--output', output_path] - dex_cmd += input_paths + dex_cmd = dex_cmd + ['--output', output_path] + input_paths build_utils.CheckOutput(dex_cmd, print_stderr=False) @@ -242,7 +172,7 @@ output_files = os.listdir(dexlayout_output_dir) if not output_files: raise Exception('dexlayout unexpectedly produced no output') - return [os.path.join(dexlayout_output_dir, f) for f in output_files] + return sorted([os.path.join(dexlayout_output_dir, f) for f in output_files]) def _ZipMultidex(file_dir, dex_files): @@ -284,37 +214,53 @@ return zip_name -def _ZipSingleDex(dex_file, zip_name): - """Zip up a single dex file. +def _ZipAligned(dex_files, output_path): + """Creates a .dex.jar with 4-byte aligned files. Args: - dex_file: A dexfile whose name is ignored. - zip_name: The output file in which to write the zip. + dex_files: List of dex files. + output_path: The output file in which to write the zip. """ - build_utils.DoZip([('classes.dex', dex_file)], zip_name) + with zipfile.ZipFile(output_path, 'w') as z: + for i, dex_file in enumerate(dex_files): + name = 'classes{}.dex'.format(i + 1 if i > 0 else '') + zip_info = build_utils.HermeticZipInfo(filename=name) + cur_offset = z.fp.tell() + header_size = _FIXED_ZIP_HEADER_LEN + len(name) + alignment_needed = (4 - cur_offset + header_size) % 4 + + # Extra field used to 4-byte align classes.dex. Alignment speeds up + # execution when dex files are used via incremental install. + zip_info.extra = b'\0' * alignment_needed + with open(dex_file) as f: + z.writestr(zip_info, f.read()) -def main(args): - options, paths = _ParseArgs(args) - if ((options.proguard_enabled == 'true' - and options.configuration_name == 'Release') - or (options.debug_build_proguard_enabled == 'true' - and options.configuration_name == 'Debug')): - paths = [options.proguard_enabled_input_path] +def _PerformDexlayout(tmp_dir, tmp_dex_output, options): + if options.proguard_mapping_path is not None: + matching_profile = os.path.join(tmp_dir, 'obfuscated_profile') + convert_dex_profile.ObfuscateProfile( + options.dexlayout_profile, tmp_dex_output, + options.proguard_mapping_path, options.dexdump_path, matching_profile) + else: + logging.warning('No obfuscation for %s', options.dexlayout_profile) + matching_profile = options.dexlayout_profile + binary_profile = _CreateBinaryProfile(matching_profile, tmp_dex_output, + options.profman_path, tmp_dir) + output_files = _LayoutDex(binary_profile, tmp_dex_output, + options.dexlayout_path, tmp_dir) + if len(output_files) > 1: + return _ZipMultidex(tmp_dir, output_files) - if options.inputs: - paths += options.inputs + if zipfile.is_zipfile(output_files[0]): + return output_files[0] - if options.excluded_paths: - # Excluded paths are relative to the output directory. - exclude_paths = options.excluded_paths - paths = [p for p in paths if not - os.path.relpath(p, options.output_directory) in exclude_paths] + final_output = os.path.join(tmp_dir, 'dex_classes.zip') + _ZipAligned(output_files, final_output) + return final_output - input_paths = list(paths) - if options.multi_dex and options.main_dex_list_path: - input_paths.append(options.main_dex_list_path) +def _PerformDexing(options): dex_cmd = ['java', '-jar', options.d8_jar_path, '--no-desugaring'] if options.multi_dex and options.main_dex_list_path: dex_cmd += ['--main-dex-list', options.main_dex_list_path] @@ -323,62 +269,39 @@ if options.min_api: dex_cmd += ['--min-api', options.min_api] - is_dex = options.dex_path.endswith('.dex') - is_jar = options.dex_path.endswith('.jar') - with build_utils.TempDir() as tmp_dir: tmp_dex_dir = os.path.join(tmp_dir, 'tmp_dex_dir') os.mkdir(tmp_dex_dir) - if is_jar and _NoClassFiles(paths): - # Handle case where no classfiles are specified in inputs - # by creating an empty JAR - with zipfile.ZipFile(options.dex_path, 'w') as outfile: - outfile.comment = 'empty' - else: - # .dex files can't specify a name for D8. Instead, we output them to a - # temp directory then move them after the command has finished running - # (see _MoveTempDexFile). For other files, tmp_dex_dir is None. - _RunD8(dex_cmd, paths, tmp_dex_dir) + _RunD8(dex_cmd, options.inputs, tmp_dex_dir) + dex_files = [os.path.join(tmp_dex_dir, f) for f in os.listdir(tmp_dex_dir)] - tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output') - if is_dex: - _MoveTempDexFile(tmp_dex_dir, tmp_dex_output) + if not options.output.endswith('.dex'): + tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output.zip') + _ZipAligned(sorted(dex_files), tmp_dex_output) else: - # d8 supports outputting to a .zip, but does not have deterministic file - # ordering: https://issuetracker.google.com/issues/119945929 - build_utils.ZipDir(tmp_dex_output, tmp_dex_dir) + # Output to a .dex file. + if len(dex_files) > 1: + raise Exception('%d files created, expected 1' % len(dex_files)) + tmp_dex_output = dex_files[0] if options.dexlayout_profile: - if options.proguard_mapping_path is not None: - matching_profile = os.path.join(tmp_dir, 'obfuscated_profile') - convert_dex_profile.ObfuscateProfile( - options.dexlayout_profile, tmp_dex_output, - options.proguard_mapping_path, options.dexdump_path, - matching_profile) - else: - logging.warning('No obfuscation for %s', options.dexlayout_profile) - matching_profile = options.dexlayout_profile - binary_profile = _CreateBinaryProfile(matching_profile, tmp_dex_output, - options.profman_path, tmp_dir) - output_files = _LayoutDex(binary_profile, tmp_dex_output, - options.dexlayout_path, tmp_dir) - target = None - if len(output_files) > 1: - target = _ZipMultidex(tmp_dir, output_files) - else: - output = output_files[0] - if not zipfile.is_zipfile(output): - target = os.path.join(tmp_dir, 'dex_classes.zip') - _ZipSingleDex(output, target) - else: - target = output - shutil.move(os.path.join(tmp_dir, target), tmp_dex_output) + tmp_dex_output = _PerformDexlayout(tmp_dir, tmp_dex_output, options) # The dex file is complete and can be moved out of tmp_dir. - shutil.move(tmp_dex_output, options.dex_path) + shutil.move(tmp_dex_output, options.output) + + +def main(args): + options = _ParseArgs(args) + + input_paths = list(options.inputs) + if options.multi_dex and options.main_dex_list_path: + input_paths.append(options.main_dex_list_path) + + _PerformDexing(options) build_utils.WriteDepfile( - options.depfile, options.dex_path, input_paths, add_pydeps=False) + options.depfile, options.output, input_paths, add_pydeps=False) if __name__ == '__main__':
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py index fabd1e3..b1f6944 100644 --- a/build/android/gyp/util/build_utils.py +++ b/build/android/gyp/util/build_utils.py
@@ -34,10 +34,6 @@ os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, os.pardir))) -HERMETIC_TIMESTAMP = (2001, 1, 1, 0, 0, 0) -_HERMETIC_FILE_ATTR = (0o644 << 16) - - try: string_types = basestring except NameError: @@ -314,6 +310,14 @@ return extracted +def HermeticZipInfo(*args, **kwargs): + """Creates a ZipInfo with a constant timestamp and external_attr.""" + ret = zipfile.ZipInfo(*args, **kwargs) + ret.date_time = (2001, 1, 1, 0, 0, 0) + ret.external_attr = (0o644 << 16) + return ret + + def AddToZipHermetic(zip_file, zip_path, src_path=None, data=None, compress=None): """Adds a file to the given ZipFile with a hard-coded modified time. @@ -329,8 +333,7 @@ assert (src_path is None) != (data is None), ( '|src_path| and |data| are mutually exclusive.') _CheckZipPath(zip_path) - zipinfo = zipfile.ZipInfo(filename=zip_path, date_time=HERMETIC_TIMESTAMP) - zipinfo.external_attr = _HERMETIC_FILE_ATTR + zipinfo = HermeticZipInfo(filename=zip_path) if src_path and os.path.islink(src_path): zipinfo.filename = zip_path
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index 00dff485..b4d8c71 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -942,14 +942,6 @@ '--resource-ids-provider', help='Path to the .build_config for the APK that this static library ' 'target uses to generate stable resource IDs.') - parser.add_option( - '--compressed-locales-provider', - help='Path to the .build_config that contains the compressed locales ' - 'Java list for this static library target.') - parser.add_option( - '--uncompressed-locales-provider', - help='Path to the .build_config that contains the uncompressed locales ' - 'Java list for this static library target.') parser.add_option('--tested-apk-config', help='Path to the build config of the tested apk (for an instrumentation ' @@ -1731,27 +1723,11 @@ config['assets'], config['uncompressed_assets'], locale_paks = ( _MergeAssets(deps.All('android_assets'))) - if options.compressed_locales_provider: - dep_config = GetDepConfig(options.compressed_locales_provider) - if dep_config['type'] == 'android_app_bundle': - dep_config = GetDepConfig(dep_config['base_module_config']) - deps_info['compressed_locales_java_list'] = dep_config[ - 'compressed_locales_java_list'] - else: - deps_info[ - 'compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( - config['assets'], locale_paks) - - if options.uncompressed_locales_provider: - dep_config = GetDepConfig(options.uncompressed_locales_provider) - if dep_config['type'] == 'android_app_bundle': - dep_config = GetDepConfig(dep_config['base_module_config']) - deps_info['uncompressed_locales_java_list'] = dep_config[ - 'uncompressed_locales_java_list'] - else: - deps_info[ - 'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( - config['uncompressed_assets'], locale_paks) + deps_info['compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( + config['assets'], locale_paks) + deps_info[ + 'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( + config['uncompressed_assets'], locale_paks) config['extra_android_manifests'] = filter(None, ( d.get('android_manifest') for d in all_resources_deps))
diff --git a/build/android/java/templates/LocaleConfig.template b/build/android/java/templates/LocaleConfig.template new file mode 100644 index 0000000..95f6051 --- /dev/null +++ b/build/android/java/templates/LocaleConfig.template
@@ -0,0 +1,27 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package PACKAGE; + +/** + * Locale configuration. Generated on a per-target basis. + */ +public class LocaleConfig { + + // Sorted list of locales that have a compressed .pak within assets. + // Stored as an array because AssetManager.list() is slow. +#if defined(COMPRESSED_LOCALE_LIST) + public static final String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST; +#else + public static final String[] COMPRESSED_LOCALES = {}; +#endif + + // Sorted list of locales that have an uncompressed .pak within assets. + // Stored as an array because AssetManager.list() is slow. +#if defined(UNCOMPRESSED_LOCALE_LIST) + public static final String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST; +#else + public static final String[] UNCOMPRESSED_LOCALES = {}; +#endif +}
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index adb09d10..a9273aa 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -474,12 +474,6 @@ if (_target.is_resource_ids_provider) { args += [ "--resource-ids-provider=$_config" ] } - if (_target.is_compressed_locales_provider) { - args += [ "--compressed-locales-provider=$_config" ] - } - if (_target.is_uncompressed_locales_provider) { - args += [ "--uncompressed-locales-provider=$_config" ] - } } args += [ "--static-library-dependent-configs=$_dependent_configs" ] } @@ -1306,13 +1300,11 @@ invoker.output, ] - _rebased_output = rebase_path(invoker.output, root_build_dir) - args = [ "--depfile", rebase_path(depfile, root_build_dir), - "--dex-path", - _rebased_output, + "--output", + rebase_path(outputs[0], root_build_dir), ] if (_proguard_enabled) { @@ -1333,7 +1325,7 @@ if (defined(invoker.input_dex_classpath)) { inputs += [ invoker.build_config ] - args += [ "--inputs=@FileArg(${invoker.input_dex_classpath})" ] + args += [ "--input-list=@FileArg(${invoker.input_dex_classpath})" ] } inputs += _dexing_jars
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index b10a5e65..bcf645ca 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -1889,7 +1889,6 @@ # Variables: # use_final_fields: True to use final fields. All other variables are # ignored when this is false. - # build_config: Path to build_config used for locale list # enable_multidex: Value for ENABLE_MULTIDEX. # min_sdk_version: Value for MIN_SDK_VERSION. # @@ -1926,15 +1925,6 @@ if (invoker.enable_multidex) { defines += [ "ENABLE_MULTIDEX" ] } - inputs = [ - invoker.build_config, - ] - _rebased_build_config = - rebase_path(invoker.build_config, root_build_dir) - defines += [ - "COMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:compressed_locales_java_list)", - "UNCOMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:uncompressed_locales_java_list)", - ] if (defined(invoker.min_sdk_version)) { defines += [ "_MIN_SDK_VERSION=${invoker.min_sdk_version}" ] } @@ -1947,6 +1937,35 @@ } } + # Creates LocaleConfig.java, a file containing the list of compressed and + # uncompressed locale .pak files in an APK. + # + # Variables: + # build_config: Path to build_config used for locale lists. + # java_package: Java package for the generated class. + template("generate_locale_config_srcjar") { + java_cpp_template(target_name) { + package_path = string_replace(invoker.java_package, ".", "/") + sources = [ + "//build/android/java/templates/LocaleConfig.template", + ] + defines = [ "PACKAGE=${invoker.java_package}" ] + if (defined(invoker.build_config)) { + forward_variables_from(invoker, + [ + "deps", + "testonly", + ]) + _rebased_build_config = + rebase_path(invoker.build_config, root_build_dir) + defines += [ + "COMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:compressed_locales_java_list)", + "UNCOMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:uncompressed_locales_java_list)", + ] + } + } + } + # Declare an Android app module target, which is used as the basis for an # Android APK or an Android app bundle module. # @@ -2025,8 +2044,7 @@ # static_library_dependent_targets: A list of scopes describing targets that # use this target as a static library. Common Java code from the targets # listed in static_library_dependent_targets will be moved into this - # target. Scope members are name, is_resource_ids_provider, - # is_compressed_locales_provider, is_uncompressed_locales_provider. + # target. Scope members are name and is_resource_ids_provider. # TODO(estevenson): Add a README for static library targets and document # additions to "deps_info" in write_build_config.py. # static_library_provider: Specifies a single target that this target will @@ -2045,6 +2063,9 @@ # enable_native_mocks: Allow native calls using # org.chromium.base.annotations.NativeMethods to be mocked in tests # (optional). + # locale_config_java_packages: Optional list of java packages. If given, a + # LocaleConfig.java file will be generated for each package, and will + # contain the list of compressed and uncompressed locale pak files. template("android_apk_or_module") { forward_variables_from(invoker, [ "testonly" ]) assert(defined(invoker.final_apk_path) || defined(invoker.name)) @@ -2268,6 +2289,8 @@ _generate_buildconfig_java = invoker.generate_buildconfig_java } + _generate_localeconfig_java = defined(invoker.locale_config_java_packages) + # JNI generation usually goes hand-in-hand with buildconfig generation. _generate_final_jni = _generate_buildconfig_java if (defined(invoker.generate_final_jni)) { @@ -2653,7 +2676,6 @@ generate_build_config_srcjar("${_template_name}__build_config_srcjar") { forward_variables_from(invoker, [ "min_sdk_version" ]) use_final_fields = true - build_config = _build_config enable_multidex = _enable_multidex if (defined(invoker.product_version_resources_dep)) { resources_version_variable = @@ -2666,6 +2688,21 @@ _srcjar_deps += [ ":${_template_name}__build_config_srcjar" ] } + if (_generate_localeconfig_java) { + foreach(_package, invoker.locale_config_java_packages) { + _locale_target_name = + "${_template_name}_${_package}__locale_config_srcjar" + generate_locale_config_srcjar("$_locale_target_name") { + build_config = _build_config + java_package = _package + deps = [ + ":$_build_config_target", + ] + } + _srcjar_deps += [ ":$_locale_target_name" ] + } + } + if (_generate_final_jni) { generate_jni_registration("${_template_name}__final_jni") { forward_variables_from(invoker, @@ -3319,6 +3356,7 @@ "loadable_modules", "manifest_package", "max_sdk_version", + "locale_config_java_packages", "min_sdk_version", "native_lib_placeholders", "native_lib_version_arg", @@ -3439,6 +3477,7 @@ "load_library_from_apk", "manifest_package", "max_sdk_version", + "locale_config_java_packages", "min_sdk_version", "native_lib_version_arg", "native_lib_version_rule",
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index e50ac81..d035b76 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -177,6 +177,26 @@ (use_gold || use_lld) && cc_wrapper == "" } +if (is_win || is_android || is_chromeos) { + # Set the path to use orderfile for linking Chrome + # Note that this is for using only one orderfile for linking + # the Chrome binary/library. + declare_args() { + chrome_orderfile_path = "" + + if (defined(default_chrome_orderfile)) { + # Allow downstream tools to set orderfile path with + # another variable. + chrome_orderfile_path = default_chrome_orderfile + } else if (is_win && is_clang && is_official_build) { + chrome_orderfile_path = "//chrome/build/chrome.$target_cpu.orderfile" + } else if (is_chromeos) { + # FIXME(tcwang): update this with autoroller. + chrome_orderfile_path = "" + } + } +} + # default_include_dirs --------------------------------------------------------- # # This is a separate config so that third_party code (which would not use the @@ -1576,12 +1596,17 @@ # For intentional fallthrough, use FALLTHROUGH; from # base/compiler_specific.h "-Wimplicit-fallthrough", - - # Thread safety analysis. See base/thread_annotations.h and - # https://clang.llvm.org/docs/ThreadSafetyAnalysis.html - "-Wthread-safety", ] + # Thread safety analysis is broken under nacl: https://crbug.com/982423. + if (!is_nacl) { + cflags += [ + # Thread safety analysis. See base/thread_annotations.h and + # https://clang.llvm.org/docs/ThreadSafetyAnalysis.html + "-Wthread-safety", + ] + } + # TODO(thakis): Enable this for more platforms, https://crbug.com/926235 # ChromeOS: http://crbug.com/940863 # Chromecast: http://crbug.com/942554 @@ -2430,3 +2455,45 @@ cflags_objcc = common_flags } } + +if (is_chromeos) { + # This config is intended to be a temporary to facilitate + # the transition to use orderfile in Chrome OS. Once orderfile + # use becomes a default in Chrome OS, this config should not + # be needed. + config("use_orderfile_for_hugepage") { + if (chrome_orderfile_path != "") { + defines = [ "CHROMEOS_ORDERFILE_USE" ] + } + } +} + +if (is_win || is_android || is_chromeos) { + # Use orderfile for linking Chrome on win, android, and Chrome OS. + # This config enables using an orderfile for linking in LLD. + config("chrome_orderfile_config") { + if (chrome_orderfile_path != "") { + assert(use_lld) + _rebased_orderfile = rebase_path(chrome_orderfile_path, root_build_dir) + if (is_android || is_chromeos) { + ldflags = [ + "-Wl,--symbol-ordering-file", + "-Wl,$_rebased_orderfile", + "-Wl,--no-warn-symbol-ordering", + ] + } else { + ldflags = [ + "/order:@$_rebased_orderfile", + + # Ignore warnings about missing functions or functions not in their + # own section. + "/ignore:4037", + "/ignore:4065", + ] + } + inputs = [ + chrome_orderfile_path, + ] + } + } +}
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index e690a66..d4e801b9 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8908346996268693152 \ No newline at end of file +8908319511199702128 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index cd33a36..7078c0a 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8908346972979601936 \ No newline at end of file +8908322498030374672 \ No newline at end of file
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index d933273..22ad643 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -240,8 +240,6 @@ "tiles/image_decode_cache_utils.h", "tiles/mipmap_util.cc", "tiles/mipmap_util.h", - "tiles/paint_worklet_image_cache.cc", - "tiles/paint_worklet_image_cache.h", "tiles/picture_layer_tiling.cc", "tiles/picture_layer_tiling.h", "tiles/picture_layer_tiling_set.cc", @@ -671,7 +669,6 @@ "tiles/gpu_image_decode_cache_unittest.cc", "tiles/image_controller_unittest.cc", "tiles/mipmap_util_unittest.cc", - "tiles/paint_worklet_image_cache_unittest.cc", "tiles/picture_layer_tiling_set_unittest.cc", "tiles/picture_layer_tiling_unittest.cc", "tiles/software_image_decode_cache_unittest.cc",
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc index 261141d..93b5d5d 100644 --- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc +++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -116,6 +116,10 @@ return base_client_->RequiresHighResToDraw(); } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const override { + return base_client_->GetPaintWorkletRecords(); + } + private: PictureLayerTilingClient* base_client_; Region invalidation_;
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index 0000c0b..0612c46 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -106,6 +106,11 @@ PictureLayerImpl::~PictureLayerImpl() { if (twin_layer_) twin_layer_->twin_layer_ = nullptr; + // We only track PaintWorklet-containing PictureLayerImpls on the pending + // tree. However this deletion may happen outside the commit flow when we are + // on the recycle tree instead, so just check !IsActiveTree(). + if (!paint_worklet_records_.empty() && !layer_tree_impl()->IsActiveTree()) + layer_tree_impl()->NotifyLayerHasPaintWorkletsChanged(this, false); layer_tree_impl()->UnregisterPictureLayerImpl(this); // Unregister for all images on the current raster source. @@ -855,6 +860,10 @@ return layer_tree_impl()->RequiresHighResToDraw(); } +const PaintWorkletRecordMap& PictureLayerImpl::GetPaintWorkletRecords() const { + return paint_worklet_records_; +} + gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const { return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale()); } @@ -1589,7 +1598,7 @@ return ImageInvalidationResult::kInvalidated; } -void PictureLayerImpl::SetPaintWorkletRecordForTesting( +void PictureLayerImpl::SetPaintWorkletRecord( scoped_refptr<PaintWorkletInput> input, sk_sp<PaintRecord> record) { DCHECK(paint_worklet_records_.find(input) != paint_worklet_records_.end()); @@ -1626,12 +1635,22 @@ void PictureLayerImpl::SetPaintWorkletInputs( const std::vector<scoped_refptr<PaintWorkletInput>>& inputs) { + bool had_paint_worklets = !paint_worklet_records_.empty(); PaintWorkletRecordMap new_records; for (const auto& input : inputs) { // Attempt to re-use an existing PaintRecord if possible. new_records[input] = std::move(paint_worklet_records_[input]); } paint_worklet_records_.swap(new_records); + + // The pending tree tracks which PictureLayerImpls have PaintWorkletInputs as + // an optimization to avoid walking all picture layers. + bool has_paint_worklets = !paint_worklet_records_.empty(); + if ((has_paint_worklets != had_paint_worklets) && + layer_tree_impl()->IsPendingTree()) { + layer_tree_impl()->NotifyLayerHasPaintWorkletsChanged(this, + has_paint_worklets); + } } std::unique_ptr<base::DictionaryValue> PictureLayerImpl::LayerAsJson() const {
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h index f122754..3870fa2 100644 --- a/cc/layers/picture_layer_impl.h +++ b/cc/layers/picture_layer_impl.h
@@ -60,6 +60,7 @@ void ReleaseTileResources() override; void RecreateTileResources() override; Region GetInvalidationRegionForDebugging() override; + gfx::Rect GetEnclosingRectInTargetSpace() const override; // PictureLayerTilingClient overrides. std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) override; @@ -69,7 +70,7 @@ const PictureLayerTiling* tiling) const override; bool HasValidTilePriorities() const override; bool RequiresHighResToDraw() const override; - gfx::Rect GetEnclosingRectInTargetSpace() const override; + const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; // ImageAnimationController::AnimationDriver overrides. bool ShouldAnimate(PaintImage::Id paint_image_id) const override; @@ -82,8 +83,6 @@ return gpu_raster_max_texture_size_; } - using PaintWorkletRecordMap = - base::flat_map<scoped_refptr<PaintWorkletInput>, sk_sp<PaintRecord>>; void UpdateRasterSource( scoped_refptr<RasterSource> raster_source, Region* new_invalidation, @@ -139,10 +138,14 @@ const Region& InvalidationForTesting() const { return invalidation_; } - void SetPaintWorkletRecordForTesting(scoped_refptr<PaintWorkletInput>, - sk_sp<PaintRecord>); + // Set the paint result (PaintRecord) for a given PaintWorkletInput. + void SetPaintWorkletRecord(scoped_refptr<PaintWorkletInput>, + sk_sp<PaintRecord>); - const PaintWorkletRecordMap& GetPaintWorkletRecordMapForTesting() const { + // Retrieve the map of PaintWorkletInputs to their painted results + // (PaintRecords). If a PaintWorkletInput has not been painted yet, it will + // map to nullptr. + const PaintWorkletRecordMap& GetPaintWorkletRecordMap() const { return paint_worklet_records_; }
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 21f0fa23..698fa42 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -5556,25 +5556,22 @@ // All inputs should be registered on the pending layer. SetupPendingTree(raster_source, gfx::Size(), Region(gfx::Rect(layer_bounds))); - EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMapForTesting().size(), 2u); - EXPECT_TRUE( - pending_layer()->GetPaintWorkletRecordMapForTesting().contains(input1)); - EXPECT_TRUE( - pending_layer()->GetPaintWorkletRecordMapForTesting().contains(input2)); + EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMap().size(), 2u); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input1)); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input2)); // Specify a record for one of the inputs. sk_sp<PaintRecord> record1 = sk_make_sp<PaintOpBuffer>(); - pending_layer()->SetPaintWorkletRecordForTesting(input1, record1); + pending_layer()->SetPaintWorkletRecord(input1, record1); // Now activate and make sure the active layer is registered as well, with the // appropriate record. ActivateTree(); - EXPECT_EQ(active_layer()->GetPaintWorkletRecordMapForTesting().size(), 2u); - auto it = active_layer()->GetPaintWorkletRecordMapForTesting().find(input1); - ASSERT_NE(it, active_layer()->GetPaintWorkletRecordMapForTesting().end()); + EXPECT_EQ(active_layer()->GetPaintWorkletRecordMap().size(), 2u); + auto it = active_layer()->GetPaintWorkletRecordMap().find(input1); + ASSERT_NE(it, active_layer()->GetPaintWorkletRecordMap().end()); EXPECT_EQ(it->second, record1); - EXPECT_TRUE( - active_layer()->GetPaintWorkletRecordMapForTesting().contains(input2)); + EXPECT_TRUE(active_layer()->GetPaintWorkletRecordMap().contains(input2)); // Committing new PaintWorkletInputs (in a new raster source) should replace // the previous ones. @@ -5588,9 +5585,8 @@ raster_source = recording_source->CreateRasterSource(); SetupPendingTree(raster_source, gfx::Size(), Region(gfx::Rect(layer_bounds))); - EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMapForTesting().size(), 1u); - EXPECT_TRUE( - pending_layer()->GetPaintWorkletRecordMapForTesting().contains(input3)); + EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMap().size(), 1u); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input3)); } } // namespace
diff --git a/cc/paint/discardable_image_map.cc b/cc/paint/discardable_image_map.cc index e5f689b1..fcf04a1 100644 --- a/cc/paint/discardable_image_map.cc +++ b/cc/paint/discardable_image_map.cc
@@ -361,7 +361,8 @@ paint_worklet_inputs_.push_back(paint_image.paint_worklet_input()); } else { // Make a note if any image was originally specified in a non-sRGB color - // space. + // space. PaintWorklets do not have the concept of a color space, so + // should not be used to accumulate either counter. SkColorSpace* source_color_space = paint_image.color_space(); color_stats_total_pixel_count_ += image_rect.size().GetCheckedArea(); color_stats_total_image_count_++; @@ -394,12 +395,20 @@ paint_image.reset_animation_sequence_id()); } - // If we are iterating images in a record shader, only track them if they - // are animated. We defer decoding of images in record shaders to skia, but - // we still need to track animated images to invalidate and advance the - // animation in cc. - bool add_image = - !only_gather_animated_images_ || paint_image.ShouldAnimate(); + bool add_image = true; + if (paint_image.IsPaintWorklet()) { + // PaintWorklet-backed images don't go through the image decode pipeline + // (they are painted pre-raster from LayerTreeHostImpl), so do not need to + // be added to the |image_set_|. + add_image = false; + } else if (only_gather_animated_images_) { + // If we are iterating images in a record shader, only track them if they + // are animated. We defer decoding of images in record shaders to skia, + // but we still need to track animated images to invalidate and advance + // the animation in cc. + add_image = paint_image.ShouldAnimate(); + } + if (add_image) { image_set_.emplace_back( DrawImage(std::move(paint_image), src_irect, filter_quality, matrix),
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc index 4a6fae21..e7ecf7ee 100644 --- a/cc/paint/discardable_image_map_unittest.cc +++ b/cc/paint/discardable_image_map_unittest.cc
@@ -752,11 +752,17 @@ content_layer_client.PaintContentsToDisplayList( ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); display_list->GenerateDiscardableImagesMetadata(); + const auto& paint_worklet_inputs = display_list->discardable_image_map().paint_worklet_inputs(); - ASSERT_EQ(paint_worklet_inputs.size(), 1u); EXPECT_EQ(paint_worklet_inputs[0], input); + + // PaintWorklets are not considered discardable images. + std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect( + display_list->discardable_image_map(), visible_rect); + ASSERT_EQ(images.size(), 1u); + EXPECT_EQ(images[0].image, static_image); } TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) { @@ -911,19 +917,6 @@ ImageAnalysisState::kAnimatedImages); } -TEST_F(DiscardableImageMapTest, BuildPaintWorkletImage) { - gfx::SizeF size(100, 50); - scoped_refptr<TestPaintWorkletInput> input = - base::MakeRefCounted<TestPaintWorkletInput>(size); - PaintImage paint_image = PaintImageBuilder::WithDefault() - .set_id(1) - .set_paint_worklet_input(std::move(input)) - .TakePaintImage(); - EXPECT_TRUE(paint_image.paint_worklet_input()); - EXPECT_EQ(paint_image.width(), size.width()); - EXPECT_EQ(paint_image.height(), size.height()); -} - TEST_F(DiscardableImageMapTest, DecodingModeHintsBasic) { gfx::Rect visible_rect(100, 100); PaintImage unspecified_image =
diff --git a/cc/paint/image_provider.cc b/cc/paint/image_provider.cc index e30c8bb..dd471cbf 100644 --- a/cc/paint/image_provider.cc +++ b/cc/paint/image_provider.cc
@@ -13,16 +13,13 @@ ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image) : image_(std::move(image)) {} +ImageProvider::ScopedResult::ScopedResult(sk_sp<PaintRecord> record) + : record_(std::move(record)) {} + ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image, DestructionCallback callback) : image_(std::move(image)), destruction_callback_(std::move(callback)) {} -ImageProvider::ScopedResult::ScopedResult(sk_sp<PaintRecord> record, - DestructionCallback callback) - : record_(std::move(record)), destruction_callback_(std::move(callback)) { - DCHECK(!destruction_callback_.is_null()); -} - ImageProvider::ScopedResult::ScopedResult(ScopedResult&& other) : image_(std::move(other.image_)), record_(std::move(other.record_)),
diff --git a/cc/paint/image_provider.h b/cc/paint/image_provider.h index 2b5e5f707..973f8f7 100644 --- a/cc/paint/image_provider.h +++ b/cc/paint/image_provider.h
@@ -27,8 +27,8 @@ ScopedResult(); explicit ScopedResult(DecodedDrawImage image); + explicit ScopedResult(sk_sp<PaintRecord> record); ScopedResult(DecodedDrawImage image, DestructionCallback callback); - ScopedResult(sk_sp<PaintRecord> record, DestructionCallback callback); ScopedResult(const ScopedResult&) = delete; ScopedResult(ScopedResult&& other); ~ScopedResult();
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h index 2f68f070..4090b749 100644 --- a/cc/paint/paint_image.h +++ b/cc/paint/paint_image.h
@@ -258,8 +258,8 @@ sk_sp<SkImage> GetSkImageForFrame(size_t index, GeneratorClientId client_id) const; - PaintWorkletInput* paint_worklet_input() const { - return paint_worklet_input_.get(); + const scoped_refptr<PaintWorkletInput>& paint_worklet_input() const { + return paint_worklet_input_; } std::string ToString() const;
diff --git a/cc/paint/paint_image_unittest.cc b/cc/paint/paint_image_unittest.cc index 82793a6..bc1a06d 100644 --- a/cc/paint/paint_image_unittest.cc +++ b/cc/paint/paint_image_unittest.cc
@@ -8,6 +8,7 @@ #include "cc/paint/paint_image_builder.h" #include "cc/test/fake_paint_image_generator.h" #include "cc/test/skia_common.h" +#include "cc/test/test_paint_worklet_input.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -156,4 +157,17 @@ yuv_generator->reset_frames_decoded(); } +TEST(PaintImageTest, BuildPaintWorkletImage) { + gfx::SizeF size(100, 50); + scoped_refptr<TestPaintWorkletInput> input = + base::MakeRefCounted<TestPaintWorkletInput>(size); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(1) + .set_paint_worklet_input(std::move(input)) + .TakePaintImage(); + EXPECT_TRUE(paint_image.paint_worklet_input()); + EXPECT_EQ(paint_image.width(), size.width()); + EXPECT_EQ(paint_image.height(), size.height()); +} + } // namespace cc
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc index f40f19a..8ccd0abb 100644 --- a/cc/paint/paint_op_buffer_unittest.cc +++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -2732,15 +2732,10 @@ ~MockImageProvider() override = default; - void DoNothing() {} - ImageProvider::ScopedResult GetRasterContent( const DrawImage& draw_image) override { - if (draw_image.paint_image().IsPaintWorklet()) { - auto callback = - base::BindOnce(&MockImageProvider::DoNothing, base::Unretained(this)); - return ScopedResult(record_, std::move(callback)); - } + if (draw_image.paint_image().IsPaintWorklet()) + return ScopedResult(record_); if (fail_all_decodes_) return ImageProvider::ScopedResult();
diff --git a/cc/paint/paint_worklet_input.h b/cc/paint/paint_worklet_input.h index dd80da7..c54238d 100644 --- a/cc/paint/paint_worklet_input.h +++ b/cc/paint/paint_worklet_input.h
@@ -5,12 +5,17 @@ #ifndef CC_PAINT_PAINT_WORKLET_INPUT_H_ #define CC_PAINT_PAINT_WORKLET_INPUT_H_ +#include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" #include "cc/paint/paint_export.h" +#include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/geometry/size_f.h" namespace cc { +class PaintOpBuffer; +using PaintRecord = PaintOpBuffer; + class CC_PAINT_EXPORT PaintWorkletInput : public base::RefCountedThreadSafe<PaintWorkletInput> { public: @@ -22,6 +27,11 @@ virtual ~PaintWorkletInput() = default; }; +// PaintWorkletRecordMap ties the input for a PaintWorklet (PaintWorkletInput) +// to the painted output (a PaintRecord). +using PaintWorkletRecordMap = + base::flat_map<scoped_refptr<PaintWorkletInput>, sk_sp<PaintRecord>>; + } // namespace cc #endif // CC_PAINT_PAINT_WORKLET_INPUT_H_
diff --git a/cc/paint/paint_worklet_job.cc b/cc/paint/paint_worklet_job.cc index 13f09da..e339853 100644 --- a/cc/paint/paint_worklet_job.cc +++ b/cc/paint/paint_worklet_job.cc
@@ -8,8 +8,9 @@ namespace cc { -PaintWorkletJob::PaintWorkletJob(scoped_refptr<PaintWorkletInput> input) - : input_(std::move(input)) {} +PaintWorkletJob::PaintWorkletJob(int layer_id, + scoped_refptr<PaintWorkletInput> input) + : layer_id_(layer_id), input_(std::move(input)) {} PaintWorkletJob::PaintWorkletJob(const PaintWorkletJob& other) = default; PaintWorkletJob::PaintWorkletJob(PaintWorkletJob&& other) = default;
diff --git a/cc/paint/paint_worklet_job.h b/cc/paint/paint_worklet_job.h index dc64133..e6b0d8a 100644 --- a/cc/paint/paint_worklet_job.h +++ b/cc/paint/paint_worklet_job.h
@@ -20,17 +20,21 @@ // and return the results to cc-impl. class CC_PAINT_EXPORT PaintWorkletJob { public: - explicit PaintWorkletJob(scoped_refptr<PaintWorkletInput> input); + PaintWorkletJob(int layer_id, scoped_refptr<PaintWorkletInput> input); PaintWorkletJob(const PaintWorkletJob& other); PaintWorkletJob(PaintWorkletJob&& other); ~PaintWorkletJob(); + int layer_id() const { return layer_id_; } const scoped_refptr<PaintWorkletInput>& input() const { return input_; } const sk_sp<PaintRecord>& output() const { return output_; } void SetOutput(sk_sp<PaintRecord> output); private: + // The id for the layer that the PaintWorkletInput is associated with. + int layer_id_; + // The input for a PaintWorkletJob is encapsulated in a PaintWorkletInput // instance; see class-level comments on |PaintWorkletInput| for details. scoped_refptr<PaintWorkletInput> input_;
diff --git a/cc/paint/paint_worklet_layer_painter.h b/cc/paint/paint_worklet_layer_painter.h index 10e18f2..4b56a60a8 100644 --- a/cc/paint/paint_worklet_layer_painter.h +++ b/cc/paint/paint_worklet_layer_painter.h
@@ -12,8 +12,6 @@ namespace cc { -class PaintWorkletInput; - // PaintWorkletLayerPainter bridges between the compositor and the PaintWorklet // thread, providing hooks for the compositor to paint PaintWorklet content that // Blink has deferred on. @@ -21,18 +19,18 @@ public: virtual ~PaintWorkletLayerPainter() {} - // Synchronously paints a PaintWorklet instance (represented by a - // PaintWorkletInput), returning the resultant PaintRecord. - // - // TODO(crbug.com/907897): Once we remove the raster thread path, we will only - // be using |DispatchWorklets| and this can be removed. - virtual sk_sp<PaintRecord> Paint(const PaintWorkletInput*) = 0; - // Asynchronously paints a set of PaintWorklet instances. The results are // returned via the provided callback, on the same thread that originally // called this method. + // + // Only one dispatch is allowed at a time; the calling code should not call + // |DispatchWorklets| again until the passed |DoneCallback| has been called. using DoneCallback = base::OnceCallback<void(PaintWorkletJobMap)>; virtual void DispatchWorklets(PaintWorkletJobMap, DoneCallback) = 0; + + // Returns whether or not a dispatched set of PaintWorklet instances is + // currently being painted. + virtual bool HasOngoingDispatch() const = 0; }; } // namespace cc
diff --git a/cc/raster/paint_worklet_image_provider.cc b/cc/raster/paint_worklet_image_provider.cc index 7312ba3..a382140 100644 --- a/cc/raster/paint_worklet_image_provider.cc +++ b/cc/raster/paint_worklet_image_provider.cc
@@ -5,15 +5,13 @@ #include "cc/raster/paint_worklet_image_provider.h" #include <utility> -#include "cc/tiles/paint_worklet_image_cache.h" +#include "base/bind_helpers.h" namespace cc { PaintWorkletImageProvider::PaintWorkletImageProvider( - PaintWorkletImageCache* cache) - : cache_(cache) { - DCHECK(cache_); -} + PaintWorkletRecordMap records) + : records_(std::move(records)) {} PaintWorkletImageProvider::~PaintWorkletImageProvider() = default; @@ -24,11 +22,12 @@ PaintWorkletImageProvider&& other) = default; ImageProvider::ScopedResult PaintWorkletImageProvider::GetPaintRecordResult( - PaintWorkletInput* input) { - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> - record_and_callback = cache_->GetPaintRecordAndRef(input); - return ImageProvider::ScopedResult(std::move(record_and_callback.first), - std::move(record_and_callback.second)); + scoped_refptr<PaintWorkletInput> input) { + // The |records_| contains all known PaintWorkletInputs, whether they are + // painted or not, so |input| should always exist in it. + auto it = records_.find(input); + DCHECK(it != records_.end()); + return ImageProvider::ScopedResult(it->second); } } // namespace cc
diff --git a/cc/raster/paint_worklet_image_provider.h b/cc/raster/paint_worklet_image_provider.h index 866a348..6204c39 100644 --- a/cc/raster/paint_worklet_image_provider.h +++ b/cc/raster/paint_worklet_image_provider.h
@@ -7,16 +7,20 @@ #include "cc/cc_export.h" #include "cc/paint/image_provider.h" +#include "cc/paint/paint_worklet_input.h" namespace cc { -class PaintWorkletImageCache; -class PaintWorkletInput; -// PaintWorkletImageProvider is a bridge between PaintWorkletImageCache and its -// rasterization. +// PaintWorkletImageProvider is a storage class for PaintWorklets and their +// painted content for use during rasterisation. +// +// PaintWorklet-based images are not painted at Blink Paint time; instead a +// placeholder PaintWorkletInput is put in place and the painting is done later +// from the cc-impl thread. By the time raster happens the resultant PaintRecord +// is available, and this class provides the lookup from input to record. class CC_EXPORT PaintWorkletImageProvider { public: - explicit PaintWorkletImageProvider(PaintWorkletImageCache* cache); + explicit PaintWorkletImageProvider(PaintWorkletRecordMap records); PaintWorkletImageProvider(const PaintWorkletImageProvider&) = delete; PaintWorkletImageProvider(PaintWorkletImageProvider&& other); ~PaintWorkletImageProvider(); @@ -25,10 +29,11 @@ delete; PaintWorkletImageProvider& operator=(PaintWorkletImageProvider&& other); - ImageProvider::ScopedResult GetPaintRecordResult(PaintWorkletInput* input); + ImageProvider::ScopedResult GetPaintRecordResult( + scoped_refptr<PaintWorkletInput> input); private: - PaintWorkletImageCache* cache_; + PaintWorkletRecordMap records_; }; } // namespace cc
diff --git a/cc/raster/raster_source_unittest.cc b/cc/raster/raster_source_unittest.cc index 0bd9e4fc..7c73489 100644 --- a/cc/raster/raster_source_unittest.cc +++ b/cc/raster/raster_source_unittest.cc
@@ -182,63 +182,6 @@ } } -TEST(RasterSourceTest, MultiPaintWorkletImages) { - gfx::Size layer_bounds(512, 512); - - std::unique_ptr<FakeRecordingSource> recording_source = - FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); - - scoped_refptr<TestPaintWorkletInput> input1 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f)); - scoped_refptr<TestPaintWorkletInput> input2 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(64.0f, 64.0f)); - scoped_refptr<TestPaintWorkletInput> input3 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100.0f, 100.0f)); - - PaintImage discardable_image[2][2]; - discardable_image[0][0] = CreatePaintWorkletPaintImage(input1); - discardable_image[0][1] = CreatePaintWorkletPaintImage(input2); - discardable_image[1][1] = CreatePaintWorkletPaintImage(input3); - - recording_source->add_draw_image(discardable_image[0][0], gfx::Point(0, 0)); - recording_source->add_draw_image(discardable_image[0][1], gfx::Point(260, 0)); - recording_source->add_draw_image(discardable_image[1][1], - gfx::Point(260, 260)); - recording_source->Rerecord(); - - scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource(); - - // Tile sized iterators. These should find only one image. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 0, 256, 256), &images); - EXPECT_EQ(1u, images.size()); - EXPECT_EQ(discardable_image[0][0], images[0]->paint_image()); - } - // Shifted tile sized iterators. These should find only one image. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(260, 260, 256, 256), &images); - EXPECT_EQ(1u, images.size()); - EXPECT_EQ(discardable_image[1][1], images[0]->paint_image()); - } - // Ensure there's no discardable pixel refs in the empty cell - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 256, 256, 256), &images); - EXPECT_EQ(0u, images.size()); - } - // Layer sized iterators. These should find three images. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 0, 512, 512), &images); - EXPECT_EQ(3u, images.size()); - EXPECT_EQ(discardable_image[0][0], images[0]->paint_image()); - EXPECT_EQ(discardable_image[0][1], images[1]->paint_image()); - EXPECT_EQ(discardable_image[1][1], images[2]->paint_image()); - } -} - TEST(RasterSourceTest, PixelRefIteratorDiscardableRefsOneTile) { gfx::Size layer_bounds(512, 512);
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index 0fa5f3047..a9e9dc4 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -2834,7 +2834,8 @@ // until they are done. state.NotifyPaintWorkletStateChange( SchedulerStateMachine::PaintWorkletState::PROCESSING); - EXPECT_DCHECK_DEATH(state.NotifyReadyToActivate()); + // We (correctly) cannot call state.NotifyReadyToActivate() here as it hits a + // DCHECK because PaintWorklets are ongoing. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); state.NotifyPaintWorkletStateChange( SchedulerStateMachine::PaintWorkletState::IDLE);
diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc index d032695..a993fbe3 100644 --- a/cc/test/fake_layer_tree_host_impl.cc +++ b/cc/test/fake_layer_tree_host_impl.cc
@@ -63,6 +63,13 @@ float arbitrary_large_page_scale = 100000.f; pending_tree()->PushPageScaleFromMainThread( 1.f, 1.f / arbitrary_large_page_scale, arbitrary_large_page_scale); + // Normally a pending tree will not be fully painted until the commit has + // happened and any PaintWorklets have been resolved. However many of the + // unittests never actually commit the pending trees that they create, so to + // enable them to still treat the tree as painted we forcibly override the + // state here. Note that this marks a distinct departure from reality in the + // name of easier testing. + set_pending_tree_fully_painted_for_testing(true); } void FakeLayerTreeHostImpl::NotifyTileStateChanged(const Tile* tile) {
diff --git a/cc/test/fake_picture_layer_tiling_client.cc b/cc/test/fake_picture_layer_tiling_client.cc index 7acb267e..4145715 100644 --- a/cc/test/fake_picture_layer_tiling_client.cc +++ b/cc/test/fake_picture_layer_tiling_client.cc
@@ -78,4 +78,9 @@ return false; } +const PaintWorkletRecordMap& +FakePictureLayerTilingClient::GetPaintWorkletRecords() const { + return paint_worklet_records_; +} + } // namespace cc
diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h index 2ebdf1d..1aad2fdd 100644 --- a/cc/test/fake_picture_layer_tiling_client.h +++ b/cc/test/fake_picture_layer_tiling_client.h
@@ -39,6 +39,7 @@ const PictureLayerTiling* GetPendingOrActiveTwinTiling( const PictureLayerTiling* tiling) const override; bool RequiresHighResToDraw() const override; + const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; void set_twin_tiling_set(PictureLayerTilingSet* set) { twin_set_ = set; @@ -70,6 +71,7 @@ gfx::Rect text_rect_; Region invalidation_; bool has_valid_tile_priorities_; + PaintWorkletRecordMap paint_worklet_records_; }; } // namespace cc
diff --git a/cc/test/fake_raster_source.cc b/cc/test/fake_raster_source.cc index 09e369b..b945ef5 100644 --- a/cc/test/fake_raster_source.cc +++ b/cc/test/fake_raster_source.cc
@@ -10,6 +10,7 @@ #include "cc/paint/paint_flags.h" #include "cc/test/fake_recording_source.h" #include "cc/test/skia_common.h" +#include "cc/test/test_paint_worklet_input.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/gfx/geometry/size.h" @@ -65,6 +66,19 @@ return base::WrapRefCounted(new FakeRasterSource(recording_source.get())); } +scoped_refptr<FakeRasterSource> FakeRasterSource::CreateFilledWithPaintWorklet( + const gfx::Size& size) { + auto recording_source = + FakeRecordingSource::CreateFilledRecordingSource(size); + + auto input = base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(size)); + recording_source->add_draw_image( + CreatePaintWorkletPaintImage(std::move(input)), gfx::Point(0, 0)); + + recording_source->Rerecord(); + return base::WrapRefCounted(new FakeRasterSource(recording_source.get())); +} + scoped_refptr<FakeRasterSource> FakeRasterSource::CreateFilledLCD( const gfx::Size& size) { auto recording_source =
diff --git a/cc/test/fake_raster_source.h b/cc/test/fake_raster_source.h index 7e6119d..e377520 100644 --- a/cc/test/fake_raster_source.h +++ b/cc/test/fake_raster_source.h
@@ -24,6 +24,8 @@ static scoped_refptr<FakeRasterSource> CreateFilled(const gfx::Size& size); static scoped_refptr<FakeRasterSource> CreateFilledWithImages( const gfx::Size& size); + static scoped_refptr<FakeRasterSource> CreateFilledWithPaintWorklet( + const gfx::Size& size); static scoped_refptr<FakeRasterSource> CreateFilledLCD(const gfx::Size& size); static scoped_refptr<FakeRasterSource> CreateFilledSolidColor( const gfx::Size& size);
diff --git a/cc/test/test_paint_worklet_layer_painter.cc b/cc/test/test_paint_worklet_layer_painter.cc index bf2b51a5..2a842ca 100644 --- a/cc/test/test_paint_worklet_layer_painter.cc +++ b/cc/test/test_paint_worklet_layer_painter.cc
@@ -13,17 +13,20 @@ TestPaintWorkletLayerPainter::~TestPaintWorkletLayerPainter() = default; -sk_sp<PaintRecord> TestPaintWorkletLayerPainter::Paint( - const PaintWorkletInput*) { - auto manual_record = sk_make_sp<PaintOpBuffer>(); - scoped_refptr<TestPaintWorkletInput> input = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100)); - PaintImage image = CreatePaintWorkletPaintImage(input); - manual_record->push<DrawImageOp>(image, 0.f, 0.f, nullptr); - return manual_record; +void TestPaintWorkletLayerPainter::DispatchWorklets( + PaintWorkletJobMap, + DoneCallback done_callback) { + // To enforce good behavior: the new callback should not be null, and the + // saved one should be consumed (via |TakeDoneCallback|) before + // |DispatchWorklets| is called again. + DCHECK(!done_callback.is_null()); + DCHECK(done_callback_.is_null()); + + done_callback_ = std::move(done_callback); } -void TestPaintWorkletLayerPainter::DispatchWorklets(PaintWorkletJobMap, - DoneCallback) {} +bool TestPaintWorkletLayerPainter::HasOngoingDispatch() const { + return !done_callback_.is_null(); +} } // namespace cc
diff --git a/cc/test/test_paint_worklet_layer_painter.h b/cc/test/test_paint_worklet_layer_painter.h index b0110ef..e100793 100644 --- a/cc/test/test_paint_worklet_layer_painter.h +++ b/cc/test/test_paint_worklet_layer_painter.h
@@ -10,15 +10,18 @@ namespace cc { -class PaintWorkletInput; - class TestPaintWorkletLayerPainter : public PaintWorkletLayerPainter { public: TestPaintWorkletLayerPainter(); ~TestPaintWorkletLayerPainter() override; - sk_sp<PaintRecord> Paint(const PaintWorkletInput*) override; void DispatchWorklets(PaintWorkletJobMap, DoneCallback) override; + bool HasOngoingDispatch() const override; + + DoneCallback TakeDoneCallback() { return std::move(done_callback_); } + + private: + DoneCallback done_callback_; }; } // namespace cc
diff --git a/cc/tiles/image_controller.cc b/cc/tiles/image_controller.cc index 2c310ca7..9f60046 100644 --- a/cc/tiles/image_controller.cc +++ b/cc/tiles/image_controller.cc
@@ -128,11 +128,6 @@ image_decode_queue_.clear(); } -void ImageController::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - paint_worklet_image_cache_.SetPaintWorkletLayerPainter(std::move(painter)); -} - void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { DCHECK(!cache_ || !cache); @@ -151,27 +146,7 @@ } } -void ImageController::ConvertPaintWorkletImagesToTask( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks) { - for (auto it = sync_decoded_images->begin(); - it != sync_decoded_images->end();) { - if (!it->paint_image().IsPaintWorklet()) { - ++it; - continue; - } - scoped_refptr<TileTask> result = - paint_worklet_image_cache_.GetTaskForPaintWorkletImage(*it); - if (result) - tasks->push_back(std::move(result)); - // Remove it so that there is no need to check whether an image is - // PaintWorklet generated or not in TileManager's - // work_to_schedule->extra_prepaint_images.insert. - it = sync_decoded_images->erase(it); - } -} - -void ImageController::ConvertDataImagesToTasks( +void ImageController::ConvertImagesToTasks( std::vector<DrawImage>* sync_decoded_images, std::vector<scoped_refptr<TileTask>>* tasks, bool* has_at_raster_images, @@ -180,10 +155,10 @@ *has_at_raster_images = false; for (auto it = sync_decoded_images->begin(); it != sync_decoded_images->end();) { - if (it->paint_image().IsPaintWorklet()) { - ++it; - continue; - } + // PaintWorklet images should not be included in this set; they have already + // been painted before raster and so do not need raster-time work. + DCHECK(!it->paint_image().IsPaintWorklet()); + ImageDecodeCache::TaskResult result = cache_->GetTaskForImageAndRef(*it, tracing_info); *has_at_raster_images |= result.IsAtRaster(); @@ -211,8 +186,8 @@ const ImageDecodeCache::TracingInfo& tracing_info) { std::vector<scoped_refptr<TileTask>> new_tasks; bool has_at_raster_images = false; - ConvertDataImagesToTasks(&images, &new_tasks, &has_at_raster_images, - tracing_info); + ConvertImagesToTasks(&images, &new_tasks, &has_at_raster_images, + tracing_info); UnrefImages(predecode_locked_images_); predecode_locked_images_ = std::move(images); return new_tasks;
diff --git a/cc/tiles/image_controller.h b/cc/tiles/image_controller.h index 864be4e0..1ec832d 100644 --- a/cc/tiles/image_controller.h +++ b/cc/tiles/image_controller.h
@@ -19,7 +19,6 @@ #include "cc/paint/draw_image.h" #include "cc/raster/tile_task.h" #include "cc/tiles/image_decode_cache.h" -#include "cc/tiles/paint_worklet_image_cache.h" namespace cc { @@ -39,32 +38,17 @@ ImageController& operator=(const ImageController&) = delete; void SetImageDecodeCache(ImageDecodeCache* cache); - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - // The name "Data images" are the images that are not generated by - // PaintWorklet. - // Build tile tasks for synchronously decoded images that are not generated by - // PaintWorklet. + // Build tile tasks for synchronously decoded images. // |sync_decoded_images| is the input. These are the images from a particular // tile, retrieved by the DiscardableImageMap. Images can be removed from the // vector under certain conditions. // |tasks| is an output, which are the built tile tasks. // |has_at_raster_images| is an output parameter. // |tracing_info| is used in tracing or UMA only. - void ConvertDataImagesToTasks( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks, - bool* has_at_raster_images, - const ImageDecodeCache::TracingInfo& tracing_info); - // TODO(crbug.com/915566): bundle all tasks into a big TaskBag. - // Build tile tasks for images that are generated by PaintWorklet. - // |sync_decoded_images| is the input, which are the images from a particular - // tile, retrieved by DiscardableImageMap. Images are removed from the vector - // once the tile task is built. - // |tasks| is an output, which are the built tile tasks. - void ConvertPaintWorkletImagesToTask( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks); + void ConvertImagesToTasks(std::vector<DrawImage>* sync_decoded_images, + std::vector<scoped_refptr<TileTask>>* tasks, + bool* has_at_raster_images, + const ImageDecodeCache::TracingInfo& tracing_info); void UnrefImages(const std::vector<DrawImage>& images); void ReduceMemoryUsage(); std::vector<scoped_refptr<TileTask>> SetPredecodeImages( @@ -90,9 +74,6 @@ } ImageDecodeCache* cache() const { return cache_; } - PaintWorkletImageCache* paint_worklet_image_cache() { - return &paint_worklet_image_cache_; - } protected: scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; @@ -128,7 +109,6 @@ base::WeakPtr<ImageController> weak_ptr_; ImageDecodeCache* cache_ = nullptr; - PaintWorkletImageCache paint_worklet_image_cache_; std::vector<DrawImage> predecode_locked_images_; static ImageDecodeRequestId s_next_image_decode_queue_id_;
diff --git a/cc/tiles/image_controller_unittest.cc b/cc/tiles/image_controller_unittest.cc index 8d34a7c..17b2001 100644 --- a/cc/tiles/image_controller_unittest.cc +++ b/cc/tiles/image_controller_unittest.cc
@@ -315,28 +315,6 @@ base::WeakPtrFactory<ImageControllerTest> weak_ptr_factory_{this}; }; -// Test that GetTasksForImagesAndRef does not generate task for PaintWorklet -// images. -TEST_F(ImageControllerTest, GetTasksForImagesAndRefForPaintWorkletImages) { - std::vector<DrawImage> images(1); - ImageDecodeCache::TracingInfo tracing_info; - - PaintImage paint_image = CreatePaintImage(100, 100); - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true), - PaintImage::kDefaultFrameIndex); - images[0] = draw_image; - - ASSERT_EQ(1u, images.size()); - - std::vector<scoped_refptr<TileTask>> tasks; - bool has_at_raster_images = false; - controller()->ConvertDataImagesToTasks(&images, &tasks, &has_at_raster_images, - tracing_info); - EXPECT_EQ(tasks.size(), 0u); -} - TEST_F(ImageControllerTest, NullControllerUnrefsImages) { std::vector<DrawImage> images(10); ImageDecodeCache::TracingInfo tracing_info;
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc deleted file mode 100644 index d0bd702c..0000000 --- a/cc/tiles/paint_worklet_image_cache.cc +++ /dev/null
@@ -1,146 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/tiles/paint_worklet_image_cache.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "cc/paint/paint_worklet_layer_painter.h" - -namespace cc { - -class PaintWorkletTaskImpl : public TileTask { - public: - PaintWorkletTaskImpl(PaintWorkletImageCache* cache, - const PaintImage& paint_image) - : TileTask(true), cache_(cache), paint_image_(paint_image) {} - PaintWorkletTaskImpl(const PaintWorkletTaskImpl&) = delete; - - PaintWorkletTaskImpl& operator=(const PaintWorkletTaskImpl&) = delete; - - // Overridden from Task: - void RunOnWorkerThread() override { cache_->PaintImageInTask(paint_image_); } - - // Overridden from TileTask: - void OnTaskCompleted() override {} - - protected: - ~PaintWorkletTaskImpl() override = default; - - private: - PaintWorkletImageCache* cache_; - PaintImage paint_image_; -}; - -PaintWorkletImageCache::PaintWorkletImageCache() {} - -PaintWorkletImageCache::~PaintWorkletImageCache() { - for (const auto& pair : records_) - DCHECK_EQ(pair.second.used_ref_count, 0u); -} - -void PaintWorkletImageCache::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - DCHECK(!painter_); - painter_ = std::move(painter); -} - -scoped_refptr<TileTask> PaintWorkletImageCache::GetTaskForPaintWorkletImage( - const DrawImage& image) { - DCHECK(painter_); - DCHECK(image.paint_image().IsPaintWorklet()); - return base::MakeRefCounted<PaintWorkletTaskImpl>(this, image.paint_image()); -} - -// TODO(xidachen): we might need to consider the animated property value and the -// PaintWorkletInput to decide whether we need to call Paint() function or not. -void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) { - // TODO(crbug.com/939009): When creating a TileTask for a given PaintImage at - // GetTaskForPaintWorkletImage, we should not create a new TileTask if there - // is already a TileTask for this PaintImage. - { - base::AutoLock hold(records_lock_); - if (records_.find(paint_image.paint_worklet_input()) != records_.end()) - return; - } - // Because the compositor could be waiting on the lock in NotifyPrepareTiles, - // we unlock here such that the compositor won't be blocked on potentially - // slow Paint function. - // TODO(xidachen): ensure that the canvas operations in the PaintRecord - // matches the PaintGeneratedImage::Draw. - sk_sp<PaintRecord> record = - painter_->Paint(paint_image.paint_worklet_input()); - if (!record) - return; - { - base::AutoLock hold(records_lock_); - // It is possible for two or more threads to both pass through the first - // lock and arrive here. To avoid ref-count issues caused by potential - // racing among threads, we use insert such that if an entry already exists - // for a particular key, the value won't be overridden. - records_.insert( - std::make_pair(paint_image.paint_worklet_input(), - PaintWorkletImageCacheValue(std::move(record), 0))); - } -} - -std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> -PaintWorkletImageCache::GetPaintRecordAndRef(PaintWorkletInput* input) { - base::AutoLock hold(records_lock_); - DCHECK(records_.find(input) != records_.end()); - records_[input].used_ref_count++; - records_[input].num_of_frames_not_accessed = 0u; - // The PaintWorkletImageCache object lives as long as the LayerTreeHostImpl, - // and that ensures that this pointer and the input will be alive when this - // callback is executed. - auto callback = - base::BindOnce(&PaintWorkletImageCache::DecrementCacheRefCount, - base::Unretained(this), base::Unretained(input)); - return std::make_pair(records_[input].record, std::move(callback)); -} - -void PaintWorkletImageCache::SetNumOfFramesToPurgeCacheEntryForTest( - size_t num) { - num_of_frames_to_purge_cache_entry_ = num; -} - -void PaintWorkletImageCache::DecrementCacheRefCount(PaintWorkletInput* input) { - base::AutoLock hold(records_lock_); - auto it = records_.find(input); - DCHECK(it != records_.end()); - - auto& pair = it->second; - DCHECK_GT(pair.used_ref_count, 0u); - pair.used_ref_count--; -} - -void PaintWorkletImageCache::NotifyDidPrepareTiles() { - base::AutoLock hold(records_lock_); - base::EraseIf( - records_, - [this]( - const std::pair<PaintWorkletInput*, PaintWorkletImageCacheValue>& t) { - return t.second.num_of_frames_not_accessed >= - num_of_frames_to_purge_cache_entry_ && - t.second.used_ref_count == 0; - }); - for (auto& pair : records_) - pair.second.num_of_frames_not_accessed++; -} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue() = default; - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue(sk_sp<PaintRecord> record, size_t ref_count) - : record(std::move(record)), used_ref_count(ref_count) {} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue(const PaintWorkletImageCacheValue& other) - : record(other.record), used_ref_count(other.used_ref_count) {} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - ~PaintWorkletImageCacheValue() = default; - -} // namespace cc
diff --git a/cc/tiles/paint_worklet_image_cache.h b/cc/tiles/paint_worklet_image_cache.h deleted file mode 100644 index f9b976f3..0000000 --- a/cc/tiles/paint_worklet_image_cache.h +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_ -#define CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_ - -#include <utility> - -#include "base/containers/flat_map.h" -#include "base/synchronization/lock.h" -#include "cc/cc_export.h" -#include "cc/paint/draw_image.h" -#include "cc/paint/paint_record.h" -#include "cc/paint/paint_worklet_layer_painter.h" -#include "cc/raster/tile_task.h" -#include "cc/tiles/image_decode_cache.h" - -namespace cc { - -// PaintWorkletImageCache is responsible for generating tasks of executing -// PaintWorklet JS paint callbacks, and being able to return the generated -// results when requested. -class CC_EXPORT PaintWorkletImageCache { - public: - struct CC_EXPORT PaintWorkletImageCacheValue { - PaintWorkletImageCacheValue(); - PaintWorkletImageCacheValue(sk_sp<PaintRecord> record, size_t ref_count); - PaintWorkletImageCacheValue(const PaintWorkletImageCacheValue&); - ~PaintWorkletImageCacheValue(); - - sk_sp<PaintRecord> record; - size_t used_ref_count; - // Indicates how many continuous frames that this cache is never accessed or - // updated. A cache entry should be purged if this number is larger than - // |num_of_frames_to_purge_cache_entry_|. - size_t num_of_frames_not_accessed = 0u; - }; - - PaintWorkletImageCache(); - - ~PaintWorkletImageCache(); - - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - - scoped_refptr<TileTask> GetTaskForPaintWorkletImage(const DrawImage& image); - - void PaintImageInTask(const PaintImage& paint_image); - - void NotifyDidPrepareTiles(); - - // Returns a callback to decrement the ref count for the corresponding entry. - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> - GetPaintRecordAndRef(PaintWorkletInput* input); - - const base::flat_map<PaintWorkletInput*, PaintWorkletImageCacheValue>& - GetRecordsForTest() { - return records_; - } - - void SetNumOfFramesToPurgeCacheEntryForTest(size_t); - - private: - void DecrementCacheRefCount(PaintWorkletInput* input); - - // This is a map of paint worklet inputs to a pair of paint record and a - // reference count. The paint record is the representation of the worklet - // output based on the input, and the reference count is the number of times - // that it is used for tile rasterization. - base::flat_map<PaintWorkletInput*, PaintWorkletImageCacheValue> records_; - - // The |records_| can be accessed from compositor and raster worker threads at - // the same time. To prevent race, we need to lock on it. - base::Lock records_lock_; - - // The PaintWorkletImageCache is owned by ImageController, which has the same - // life time as the LayerTreeHostImpl, that guarantees that the painter will - // live as long as the LayerTreeHostImpl. - std::unique_ptr<PaintWorkletLayerPainter> painter_; - - size_t num_of_frames_to_purge_cache_entry_ = 5u; -}; - -} // namespace cc - -#endif // CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_
diff --git a/cc/tiles/paint_worklet_image_cache_unittest.cc b/cc/tiles/paint_worklet_image_cache_unittest.cc deleted file mode 100644 index 7af4b93..0000000 --- a/cc/tiles/paint_worklet_image_cache_unittest.cc +++ /dev/null
@@ -1,261 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <memory> -#include <utility> - -#include "cc/tiles/paint_worklet_image_cache.h" - -#include "cc/paint/draw_image.h" -#include "cc/raster/paint_worklet_image_provider.h" -#include "cc/test/skia_common.h" -#include "cc/test/test_paint_worklet_input.h" -#include "cc/test/test_paint_worklet_layer_painter.h" -#include "cc/test/test_tile_task_runner.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { -namespace { - -class TestPaintWorkletImageCache : public PaintWorkletImageCache { - public: - TestPaintWorkletImageCache() {} -}; - -SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { - SkMatrix matrix; - matrix.setScale(scale.width(), scale.height()); - - if (!is_decomposable) { - // Perspective is not decomposable, add it. - matrix[SkMatrix::kMPersp0] = 0.1f; - } - - return matrix; -} - -PaintImage CreatePaintImage(int width, int height) { - scoped_refptr<TestPaintWorkletInput> input = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(width, height)); - return CreatePaintWorkletPaintImage(input); -} - -scoped_refptr<TileTask> GetTaskForPaintWorkletImage( - const PaintImage& paint_image, - TestPaintWorkletImageCache* cache) { - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true), - PaintImage::kDefaultFrameIndex); - return cache->GetTaskForPaintWorkletImage(draw_image); -} - -void TestPaintRecord(const PaintRecord* record) { - EXPECT_EQ(record->total_op_count(), 1u); - - // GetOpAtForTesting check whether the type is the same as DrawImageOp or not. - // If not, it returns a nullptr. - auto* paint_op = record->GetOpAtForTesting<DrawImageOp>(0); - EXPECT_TRUE(paint_op); -} - -TEST(PaintWorkletImageCacheTest, GetTaskForImage) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - PaintWorkletImageProvider provider(&cache); - - TestTileTaskRunner::ProcessTask(task.get()); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - EXPECT_TRUE(result.paint_record()); - TestPaintRecord(result.paint_record()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count, which should have been decremented when the result - // goes out of the scope. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - ImageProvider::ScopedResult moved_result = std::move(result); - - EXPECT_FALSE(result); - - EXPECT_TRUE(moved_result.paint_record()); - TestPaintRecord(moved_result.paint_record()); - - // Once moved, the ref count from |result| should have been transferred to - // |moved_result|, so there should be only one un-ref when they both go out - // of scope. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u); -} - -TEST(PaintWorkletImageCacheTest, EntryWithNonZeroRefCountNotPurged) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - - TestTileTaskRunner::ProcessTask(task.get()); - - PaintWorkletImageProvider provider(&cache); - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - - records = cache.GetRecordsForTest(); - EXPECT_EQ(records.size(), 1u); -} - -TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image1 = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task1 = - GetTaskForPaintWorkletImage(paint_image1, &cache); - EXPECT_TRUE(task1); - PaintImage paint_image2 = CreatePaintImage(200, 200); - scoped_refptr<TileTask> task2 = - GetTaskForPaintWorkletImage(paint_image2, &cache); - EXPECT_TRUE(task2); - - TestTileTaskRunner::ProcessTask(task1.get()); - TestTileTaskRunner::ProcessTask(task2.get()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - EXPECT_EQ(records.size(), 2u); - - cache.SetNumOfFramesToPurgeCacheEntryForTest(2u); - PaintRecord* record1 = - records[paint_image1.paint_worklet_input()].record.get(); - EXPECT_TRUE(record1); - // Test the |num_of_frames_not_accessed| for this cache entry. - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - TestPaintRecord(record1); - - PaintRecord* record2 = - records[paint_image2.paint_worklet_input()].record.get(); - EXPECT_TRUE(record2); - // Test the |num_of_frames_not_accessed| for this cache entry. - EXPECT_EQ( - records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - TestPaintRecord(record2); - - // NotifyDidPrepareTiles is called by TileManager::PrepareTiles() which is - // called at each new impl frame. Here we test that a paint record with - // |num_of_frames_not_accessed| >= 2 is purged from the cache. - cache.NotifyDidPrepareTiles(); - records = cache.GetRecordsForTest(); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 1u); - EXPECT_EQ( - records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed, - 1u); - - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> pair = - cache.GetPaintRecordAndRef(paint_image1.paint_worklet_input()); - // Run the callback to decrement the ref count. - std::move(pair.second).Run(); - records = cache.GetRecordsForTest(); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - records = cache.GetRecordsForTest(); - // The cache entry for paint_image2 should have been purged because it was - // never accessed/updated in the last 2 frames. - EXPECT_EQ(records.size(), 1u); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 2u); -} - -// This test ensures that if an entry already exist, then the PaintImageInTask -// will not replace it with a new entry and reset its ref count. -TEST(PaintWorkletImageCacheTest, CacheEntryLookup) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - PaintWorkletImageProvider provider(&cache); - - TestTileTaskRunner::ProcessTask(task.get()); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - EXPECT_TRUE(result.paint_record()); - TestPaintRecord(result.paint_record()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - // Create a new task with the same PaintWorkletInput as the previous task. - // Then ProcessTask will invoke PaintWorkletImageCache::PaintImageInTask, - // and it should early exit, without replacing the existing PaintRecord and - // resetting the ref count. - scoped_refptr<TileTask> task_with_the_same_input = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - TestTileTaskRunner::ProcessTask(task_with_the_same_input.get()); - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } -} - -} // namespace -} // namespace cc
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h index 417fb14..253fdea 100644 --- a/cc/tiles/picture_layer_tiling.h +++ b/cc/tiles/picture_layer_tiling.h
@@ -17,6 +17,7 @@ #include "cc/base/region.h" #include "cc/base/tiling_data.h" #include "cc/cc_export.h" +#include "cc/paint/paint_worklet_input.h" #include "cc/tiles/tile.h" #include "cc/tiles/tile_priority.h" #include "cc/trees/occlusion.h" @@ -48,6 +49,7 @@ const PictureLayerTiling* tiling) const = 0; virtual bool HasValidTilePriorities() const = 0; virtual bool RequiresHighResToDraw() const = 0; + virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0; protected: virtual ~PictureLayerTilingClient() {} @@ -134,6 +136,9 @@ const scoped_refptr<RasterSource>& raster_source() const { return raster_source_; } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const { + return client_->GetPaintWorkletRecords(); + } gfx::Size tiling_size() const { return tiling_data_.tiling_size(); } gfx::Rect live_tiles_rect() const { return live_tiles_rect_; } gfx::Size tile_size() const { return tiling_data_.max_texture_size(); }
diff --git a/cc/tiles/prioritized_tile.h b/cc/tiles/prioritized_tile.h index a299660..5453889 100644 --- a/cc/tiles/prioritized_tile.h +++ b/cc/tiles/prioritized_tile.h
@@ -6,6 +6,7 @@ #define CC_TILES_PRIORITIZED_TILE_H_ #include "cc/cc_export.h" +#include "cc/paint/paint_worklet_input.h" #include "cc/raster/raster_source.h" #include "cc/tiles/picture_layer_tiling.h" #include "cc/tiles/tile.h" @@ -32,6 +33,9 @@ const scoped_refptr<RasterSource>& raster_source() const { return source_tiling_->raster_source(); } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const { + return source_tiling_->GetPaintWorkletRecords(); + } const TilePriority& priority() const { return priority_; } bool is_occluded() const { return is_occluded_; } bool is_process_for_images_only() const {
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc index 2374e79..032b487 100644 --- a/cc/tiles/tile_manager.cc +++ b/cc/tiles/tile_manager.cc
@@ -548,8 +548,6 @@ // Schedule tile tasks. ScheduleTasks(std::move(prioritized_work)); - image_controller_.paint_worklet_image_cache()->NotifyDidPrepareTiles(); - TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD, "state", BasicStateAsValue()); return true; @@ -1192,10 +1190,8 @@ prepare_tiles_count_, prioritized_tile.priority().priority_bin, ImageDecodeCache::TaskType::kInRaster); bool has_at_raster_images = false; - image_controller_.ConvertDataImagesToTasks( - &sync_decoded_images, &decode_tasks, &has_at_raster_images, tracing_info); - image_controller_.ConvertPaintWorkletImagesToTask(&sync_decoded_images, - &decode_tasks); + image_controller_.ConvertImagesToTasks(&sync_decoded_images, &decode_tasks, + &has_at_raster_images, tracing_info); // Notify |decoded_image_tracker_| after |image_controller_| to ensure we've // taken new refs on the images before releasing the predecode API refs. decoded_image_tracker_.OnImagesUsedInDraw(sync_decoded_images); @@ -1248,8 +1244,13 @@ PlaybackImageProvider image_provider(image_controller_.cache(), raster_color_space, std::move(settings)); + // We make a deliberate copy of the PaintWorklet map here, as the + // PictureLayerImpl's map could be mutated or destroyed whilst raster from an + // earlier snapshot is still ongoing on the raster worker threads. + PaintWorkletRecordMap paint_worklet_records = + prioritized_tile.GetPaintWorkletRecords(); PaintWorkletImageProvider paint_worklet_image_provider( - image_controller_.paint_worklet_image_cache()); + std::move(paint_worklet_records)); DispatchingImageProvider dispatching_image_provider( std::move(image_provider), std::move(paint_worklet_image_provider)); @@ -1713,11 +1714,6 @@ return std::move(state); } -void TileManager::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - image_controller_.SetPaintWorkletLayerPainter(std::move(painter)); -} - void TileManager::ActivationStateAsValueInto( base::trace_event::TracedValue* state) { state->SetString("tree_priority",
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h index 72c6209..afa7d42 100644 --- a/cc/tiles/tile_manager.h +++ b/cc/tiles/tile_manager.h
@@ -294,9 +294,6 @@ void set_active_url(const GURL& url) { active_url_ = url; } - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - protected: friend class Tile; // Must be called by tile during destruction.
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 3df571f..a548d59e 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -531,6 +531,97 @@ if (CommitToActiveTree()) ActivateStateForImages(); + if (!paint_worklet_painter_) { + // Blink should not send us any PaintWorklet inputs until we have a painter + // registered. + DCHECK(sync_tree()->picture_layers_with_paint_worklets().empty()); + pending_tree_fully_painted_ = true; + NotifyPendingTreeFullyPainted(); + return; + } + + PaintWorkletJobMap dirty_paint_worklets = GatherDirtyPaintWorklets(); + if (!dirty_paint_worklets.size()) { + pending_tree_fully_painted_ = true; + NotifyPendingTreeFullyPainted(); + return; + } + + client_->NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState::PROCESSING); + auto done_callback = base::BindOnce( + &LayerTreeHostImpl::OnPaintWorkletResultsReady, base::Unretained(this)); + paint_worklet_painter_->DispatchWorklets(std::move(dirty_paint_worklets), + std::move(done_callback)); +} + +PaintWorkletJobMap LayerTreeHostImpl::GatherDirtyPaintWorklets() const { + PaintWorkletJobMap dirty_paint_worklets; + for (PictureLayerImpl* layer : + sync_tree()->picture_layers_with_paint_worklets()) { + for (const auto& entry : layer->GetPaintWorkletRecordMap()) { + // If we already have a record we can reuse it and so the + // PaintWorkletInput isn't dirty. + if (entry.second) + continue; + + int id = entry.first->WorkletId(); + auto result = dirty_paint_worklets.insert( + std::make_pair(id, scoped_refptr<PaintWorkletJobVector>{})); + scoped_refptr<PaintWorkletJobVector>& job_vector = result.first->second; + if (!job_vector) + job_vector = base::WrapRefCounted(new PaintWorkletJobVector); + job_vector->data.emplace_back(layer->id(), entry.first); + } + } + return dirty_paint_worklets; +} + +void LayerTreeHostImpl::OnPaintWorkletResultsReady(PaintWorkletJobMap results) { + // Nothing else should have painted the PaintWorklets while we were waiting, + // and the results should have painted every PaintWorklet, so these should be + // the same. + DCHECK_EQ(results.size(), GatherDirtyPaintWorklets().size()); + + for (const auto& entry : results) { + for (const PaintWorkletJob& job : entry.second->data) { + LayerImpl* layer_impl = + pending_tree_->FindPendingTreeLayerById(job.layer_id()); + // Painting the pending tree occurs asynchronously but stalls the pending + // tree pipeline, so nothing should have changed while we were doing that. + DCHECK(layer_impl); + static_cast<PictureLayerImpl*>(layer_impl) + ->SetPaintWorkletRecord(job.input(), job.output()); + } + } + + // While the pending tree is being painted by PaintWorklets, we restrict the + // tiles the TileManager is able to see. This may cause the TileManager to + // believe that it has finished rastering all the necessary tiles. When we + // finish painting the tree and release all the tiles, we need to mark the + // tile priorities as dirty so that the TileManager logic properly re-runs. + tile_priorities_dirty_ = true; + + // Set the painted state before calling the scheduler, to ensure any callback + // running as a result sees the correct painted state. + pending_tree_fully_painted_ = true; + client_->NotifyPaintWorkletStateChange(Scheduler::PaintWorkletState::IDLE); + + // The pending tree may have been force activated from the signal to the + // scheduler above, in which case there is no longer a tree to paint. + if (pending_tree_) + NotifyPendingTreeFullyPainted(); +} + +void LayerTreeHostImpl::NotifyPendingTreeFullyPainted() { + // The pending tree must be fully painted at this point. + DCHECK(pending_tree_fully_painted_); + + // Nobody should claim the pending tree is fully painted if there is an + // ongoing dispatch. + DCHECK(!paint_worklet_painter_ || + !paint_worklet_painter_->HasOngoingDispatch()); + // Start working on newly created tiles immediately if needed. // TODO(vmpstr): Investigate always having PrepareTiles issue // NotifyReadyToActivate, instead of handling it here. @@ -1589,11 +1680,12 @@ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "LayerTreeHostImpl::BuildRasterQueue"); - return RasterTilePriorityQueue::Create(active_tree_->picture_layers(), - pending_tree_ - ? pending_tree_->picture_layers() - : std::vector<PictureLayerImpl*>(), - tree_priority, type); + return RasterTilePriorityQueue::Create( + active_tree_->picture_layers(), + pending_tree_ && pending_tree_fully_painted_ + ? pending_tree_->picture_layers() + : std::vector<PictureLayerImpl*>(), + tree_priority, type); } std::unique_ptr<EvictionTilePriorityQueue> @@ -1667,6 +1759,13 @@ } void LayerTreeHostImpl::NotifyReadyToActivate() { + // The TileManager may call this method while the pending tree is still being + // painted, as it isn't aware of the ongoing paint. We shouldn't tell the + // scheduler we are ready to activate in that case, as if we do it will + // immediately activate once we call NotifyPaintWorkletStateChange, rather + // than wait for the TileManager to actually raster the content! + if (!pending_tree_fully_painted_) + return; pending_tree_raster_duration_timer_.reset(); client_->NotifyReadyToActivate(); } @@ -2803,6 +2902,7 @@ active_tree()->top_controls_shown_ratio(), active_tree()->elastic_overscroll()); } + pending_tree_fully_painted_ = false; client_->OnCanDrawStateChanged(CanDraw()); TRACE_EVENT_ASYNC_BEGIN0("cc", "PendingTree:waiting", pending_tree_.get()); @@ -3199,7 +3299,7 @@ void LayerTreeHostImpl::SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) { - tile_manager_.SetPaintWorkletLayerPainter(std::move(painter)); + paint_worklet_painter_ = std::move(painter); } LayerImpl* LayerTreeHostImpl::ViewportMainScrollLayer() {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index b51c720..efdd9419 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h
@@ -27,6 +27,7 @@ #include "cc/input/scrollbar_animation_controller.h" #include "cc/input/scrollbar_controller.h" #include "cc/layers/layer_collections.h" +#include "cc/paint/paint_worklet_job.h" #include "cc/resources/ui_resource_client.h" #include "cc/scheduler/begin_frame_tracker.h" #include "cc/scheduler/commit_earlyout_reason.h" @@ -736,8 +737,12 @@ float autoscroll_velocity); void SetLayerTreeMutator(std::unique_ptr<LayerTreeMutator> mutator); + void SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter); + PaintWorkletLayerPainter* GetPaintWorkletLayerPainterForTesting() const { + return paint_worklet_painter_.get(); + } // The viewport has two scroll nodes, corresponding to the visual and layout // viewports. However, when we compute the scroll chain we include only one @@ -774,6 +779,10 @@ return compositor_frame_reporting_controller_.get(); } + void set_pending_tree_fully_painted_for_testing(bool painted) { + pending_tree_fully_painted_ = painted; + } + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -843,6 +852,18 @@ // impl thread. void UpdateSyncTreeAfterCommitOrImplSideInvalidation(); + // Returns a job map for all 'dirty' PaintWorklets, e.g. PaintWorkletInputs + // that do not map to a PaintRecord. + PaintWorkletJobMap GatherDirtyPaintWorklets() const; + + // Called when all PaintWorklet results are ready (i.e. have been painted) for + // the current pending tree. + void OnPaintWorkletResultsReady(PaintWorkletJobMap results); + + // Called when the pending tree has been fully painted, i.e. all required data + // is available to raster the tree. + void NotifyPendingTreeFullyPainted(); + // Returns true if status changed. bool UpdateGpuRasterizationStatus(); void UpdateTreeResourcesForGpuRasterizationIfNeeded(); @@ -1215,6 +1236,15 @@ // once the animation is over. base::Optional<ScrollState> deferred_scroll_end_state_; + // PaintWorklet painting is controlled from the LayerTreeHostImpl, dispatched + // to the worklet thread via |paint_worklet_painter_|. + std::unique_ptr<PaintWorkletLayerPainter> paint_worklet_painter_; + + // While PaintWorklet painting is ongoing the PendingTree is not yet fully + // painted and cannot be rastered or activated. This boolean tracks whether or + // not we are in that state. + bool pending_tree_fully_painted_ = false; + // Must be the last member to ensure this is destroyed first in the // destruction order and invalidates all weak pointers. base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index a932192..33e5804 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -56,6 +56,7 @@ #include "cc/test/layer_tree_test.h" #include "cc/test/skia_common.h" #include "cc/test/test_layer_tree_frame_sink.h" +#include "cc/test/test_paint_worklet_layer_painter.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" @@ -135,6 +136,7 @@ did_request_redraw_(false), did_request_next_frame_(false), did_request_prepare_tiles_(false), + did_prepare_tiles_(false), did_complete_page_scale_animation_(false), reduce_memory_result_(true), did_request_impl_side_invalidation_(false) { @@ -159,6 +161,13 @@ host_impl_->active_tree()->GetDeviceViewport().size()); pending_tree->SetDeviceScaleFactor( host_impl_->active_tree()->device_scale_factor()); + // Normally a pending tree will not be fully painted until the commit has + // happened and any PaintWorklets have been resolved. However many of the + // unittests never actually commit the pending trees that they create, so to + // enable them to still treat the tree as painted we forcibly override the + // state here. Note that this marks a distinct departure from reality in the + // name of easier testing. + host_impl_->set_pending_tree_fully_painted_for_testing(true); } void TearDown() override { @@ -203,7 +212,7 @@ base::TimeTicks::Now())); } void WillPrepareTiles() override {} - void DidPrepareTiles() override {} + void DidPrepareTiles() override { did_prepare_tiles_ = true; } void DidCompletePageScaleAnimationOnImplThread() override { did_complete_page_scale_animation_ = true; } @@ -798,6 +807,7 @@ bool did_request_redraw_; bool did_request_next_frame_; bool did_request_prepare_tiles_; + bool did_prepare_tiles_; bool did_complete_page_scale_animation_; bool reduce_memory_result_; bool did_request_impl_side_invalidation_; @@ -14820,5 +14830,194 @@ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); } +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, + CommitWithNoPaintWorkletLayerPainter) { + ASSERT_FALSE(host_impl_->GetPaintWorkletLayerPainterForTesting()); + host_impl_->CreatePendingTree(); + + // When there is no PaintWorkletLayerPainter registered, commits should finish + // immediately and move onto preparing tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithNoPaintWorklets) { + host_impl_->SetPaintWorkletLayerPainter( + std::make_unique<TestPaintWorkletLayerPainter>()); + host_impl_->CreatePendingTree(); + + // When there are no PaintWorklets in the committed display lists, commits + // should finish immediately and move onto preparing tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithDirtyPaintWorklets) { + auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); + TestPaintWorkletLayerPainter* painter = painter_owned.get(); + host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); + + // Setup the pending tree with a PictureLayerImpl that will contain + // PaintWorklets. + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add a PaintWorkletInput to the PictureLayerImpl. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Since we have dirty PaintWorklets, committing will not cause tile + // preparation to happen. Instead, it will be delayed until the callback + // passed to the PaintWorkletLayerPainter is called. + did_prepare_tiles_ = false; + host_impl_->CommitComplete(); + EXPECT_FALSE(did_prepare_tiles_); + + // Set up a result to have been 'painted'. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + scoped_refptr<PaintWorkletInput> input = + root->GetPaintWorkletRecordMap().begin()->first; + int worklet_id = input->WorkletId(); + + PaintWorkletJob painted_job(worklet_id, input); + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + painted_job.SetOutput(record); + + auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); + painted_job_vector->data.push_back(std::move(painted_job)); + PaintWorkletJobMap painted_job_map; + painted_job_map[worklet_id] = std::move(painted_job_vector); + + // Finally, 'paint' the content. This should unlock tile preparation and + // update the PictureLayerImpl's map. + std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); + EXPECT_EQ(root->GetPaintWorkletRecordMap().find(input)->second, record); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, + CommitWithNoDirtyPaintWorklets) { + host_impl_->SetPaintWorkletLayerPainter( + std::make_unique<TestPaintWorkletLayerPainter>()); + + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add some PaintWorklets. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Pretend that our worklets were already painted. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + root->SetPaintWorkletRecord(root->GetPaintWorkletRecordMap().begin()->first, + sk_make_sp<PaintRecord>()); + + // Since there are no dirty PaintWorklets, the commit should immediately + // prepare tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +class ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest + : public CommitToPendingTreeLayerTreeHostImplTest { + public: + void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) override { + if (state == Scheduler::PaintWorkletState::IDLE) { + // Pretend a force activation happened. + host_impl_->ActivateSyncTree(); + ASSERT_FALSE(host_impl_->pending_tree()); + } + } +}; + +TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest, + ForceActivationAfterPaintWorkletsFinishPainting) { + auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); + TestPaintWorkletLayerPainter* painter = painter_owned.get(); + host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); + + // Setup the pending tree with a PictureLayerImpl that will contain + // PaintWorklets. + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add a PaintWorkletInput to the PictureLayerImpl. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Since we have dirty PaintWorklets, committing will not cause tile + // preparation to happen. Instead, it will be delayed until the callback + // passed to the PaintWorkletLayerPainter is called. + did_prepare_tiles_ = false; + host_impl_->CommitComplete(); + EXPECT_FALSE(did_prepare_tiles_); + + // Set up a result to have been 'painted'. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + scoped_refptr<PaintWorkletInput> input = + root->GetPaintWorkletRecordMap().begin()->first; + int worklet_id = input->WorkletId(); + + PaintWorkletJob painted_job(worklet_id, input); + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + painted_job.SetOutput(record); + + auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); + painted_job_vector->data.push_back(std::move(painted_job)); + PaintWorkletJobMap painted_job_map; + painted_job_map[worklet_id] = std::move(painted_job_vector); + + // Finally, 'paint' the content. The test class causes a forced activation + // during NotifyPaintWorkletStateChange. The PictureLayerImpl should still be + // updated, but since the tree was force activated there should be no tile + // preparation. + std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); + EXPECT_EQ(root->GetPaintWorkletRecordMap().find(input)->second, record); + EXPECT_FALSE(did_prepare_tiles_); +} + } // namespace } // namespace cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 968b580..cee986a 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc
@@ -1867,6 +1867,23 @@ auto it = std::find(picture_layers_.begin(), picture_layers_.end(), layer); DCHECK(it != picture_layers_.end()); picture_layers_.erase(it); + + // Make sure that |picture_layers_with_paint_worklets_| doesn't get left with + // dead layers. They should already have been removed (via calling + // NotifyLayerHasPaintWorkletsChanged) before the layer was unregistered. + DCHECK(!picture_layers_with_paint_worklets_.contains(layer)); +} + +void LayerTreeImpl::NotifyLayerHasPaintWorkletsChanged(PictureLayerImpl* layer, + bool has_worklets) { + if (has_worklets) { + auto insert_pair = picture_layers_with_paint_worklets_.insert(layer); + DCHECK(insert_pair.second); + } else { + auto it = picture_layers_with_paint_worklets_.find(layer); + DCHECK(it != picture_layers_with_paint_worklets_.end()); + picture_layers_with_paint_worklets_.erase(it); + } } void LayerTreeImpl::RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer) {
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 842b5556..431f7b4 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h
@@ -542,6 +542,13 @@ return picture_layers_; } + void NotifyLayerHasPaintWorkletsChanged(PictureLayerImpl* layer, + bool has_worklets); + const base::flat_set<PictureLayerImpl*>& picture_layers_with_paint_worklets() + const { + return picture_layers_with_paint_worklets_; + } + void RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); void UnregisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const; @@ -745,6 +752,11 @@ std::vector<PictureLayerImpl*> picture_layers_; + // After commit (or impl-side invalidation), the LayerTreeHostImpl must walk + // all PictureLayerImpls that have PaintWorklets to ensure they are painted. + // To avoid unnecessary walking, we track that set here. + base::flat_set<PictureLayerImpl*> picture_layers_with_paint_worklets_; + base::flat_set<viz::SurfaceRange> surface_layer_ranges_; // List of render surfaces for the most recently prepared frame.
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc index 4dc4634..42111df 100644 --- a/cc/trees/layer_tree_impl_unittest.cc +++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -6,6 +6,7 @@ #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_raster_source.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_test_common.h" #include "cc/trees/clip_node.h" @@ -2468,6 +2469,61 @@ } } +TEST_F(LayerTreeImplTest, TrackPictureLayersWithPaintWorklets) { + host_impl().CreatePendingTree(); + LayerTreeImpl* pending_tree = host_impl().pending_tree(); + + // Initially there are no layers in the set. + EXPECT_EQ(pending_tree->picture_layers_with_paint_worklets().size(), 0u); + + // Add three layers; two with PaintWorklets and one without. + std::unique_ptr<PictureLayerImpl> child1_owned = + PictureLayerImpl::Create(pending_tree, 2, Layer::LayerMaskType::NOT_MASK); + child1_owned->SetBounds(gfx::Size(100, 100)); + std::unique_ptr<PictureLayerImpl> child2_owned = + PictureLayerImpl::Create(pending_tree, 3, Layer::LayerMaskType::NOT_MASK); + child2_owned->SetBounds(gfx::Size(100, 100)); + std::unique_ptr<PictureLayerImpl> child3_owned = + PictureLayerImpl::Create(pending_tree, 4, Layer::LayerMaskType::NOT_MASK); + child3_owned->SetBounds(gfx::Size(100, 100)); + + PictureLayerImpl* child1 = child1_owned.get(); + PictureLayerImpl* child3 = child3_owned.get(); + + root_layer()->test_properties()->AddChild(std::move(child1_owned)); + root_layer()->test_properties()->AddChild(std::move(child2_owned)); + root_layer()->test_properties()->AddChild(std::move(child3_owned)); + + Region empty_invalidation; + scoped_refptr<RasterSource> raster_source1( + FakeRasterSource::CreateFilledWithPaintWorklet(child1->bounds())); + child1->UpdateRasterSource(raster_source1, &empty_invalidation, nullptr, + nullptr); + scoped_refptr<RasterSource> raster_source3( + FakeRasterSource::CreateFilledWithPaintWorklet(child3->bounds())); + child3->UpdateRasterSource(raster_source3, &empty_invalidation, nullptr, + nullptr); + + // The set should correctly track which layers are in it. + const base::flat_set<PictureLayerImpl*>& layers = + pending_tree->picture_layers_with_paint_worklets(); + EXPECT_EQ(layers.size(), 2u); + EXPECT_TRUE(layers.contains(child1)); + EXPECT_TRUE(layers.contains(child3)); + + // Test explicitly removing a layer from the set. + scoped_refptr<RasterSource> empty_raster_source( + FakeRasterSource::CreateFilled(child1->bounds())); + child1->UpdateRasterSource(empty_raster_source, &empty_invalidation, nullptr, + nullptr); + EXPECT_EQ(layers.size(), 1u); + EXPECT_FALSE(layers.contains(child1)); + + // Deleting a layer should also cause it to be removed from the set. + root_layer()->test_properties()->RemoveChild(child3); + EXPECT_EQ(layers.size(), 0u); +} + namespace { class CommitToPendingTreeLayerTreeImplTestSettings : public LayerTreeSettings { public:
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index 87e6e9c..5e76453 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -335,6 +335,7 @@ if (is_chromeos) { data_deps += [ "//mojo/core:shared_libraries_for_arc" ] configs += [ "//build/config/chromeos:print_orderfile" ] + configs += [ "//build/config/compiler:chrome_orderfile_config" ] } # These files are used by the installer so we need a public dep. @@ -514,21 +515,7 @@ ] } - if (is_clang && is_official_build) { - orderfile = "build/chrome.$target_cpu.orderfile" - rebased_orderfile = rebase_path(orderfile, root_build_dir) - inputs = [ - orderfile, - ] - ldflags += [ - "/order:@$rebased_orderfile", - - # Ignore warnings about missing functions or functions not in their - # own section. - "/ignore:4037", - "/ignore:4065", - ] - } + configs += [ "//build/config/compiler:chrome_orderfile_config" ] } if (is_multi_dll_chrome) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 1f9ccafd..88b6bd2 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -394,6 +394,7 @@ ":chrome_android_java_enums_srcjar", ":chrome_android_java_switches_srcjar", ":chrome_android_java_google_api_keys_srcjar", + ":chrome_locale_config", ":photo_picker_aidl", ":resource_id_javagen", "//chrome:assist_ranker_prediction_enum_javagen", @@ -462,7 +463,10 @@ # Add the actual implementation where necessary so that downstream targets # can provide their own implementations. - jar_excluded_patterns = [ "*/AppHooksImpl.class" ] + jar_excluded_patterns = [ + "*/AppHooksImpl.class", + "*/LocaleConfig.class", + ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor", @@ -490,6 +494,10 @@ processor_args_javac = [ "dagger.fastInit=enabled" ] } +generate_locale_config_srcjar("chrome_locale_config") { + java_package = "org.chromium.chrome.browser" +} + # This is a list of all base module java dependencies. New features should be # added to this list. java_group("chrome_all_java") { @@ -1142,26 +1150,6 @@ variables = [ "manifest_package=$chrome_public_test_manifest_package" ] } -config("orderfile_config") { - if (chrome_orderfile != "") { - _rebased_orderfile = rebase_path(chrome_orderfile, root_build_dir) - if (use_lld) { - # Code reordering is supported only for LLD. This is because orderfile - # generator does not expand symbol names to section names. Without - # section names like 'text.hot' or '.text.unlikely' the Gold linker cannot - # produce a proper ordering. - ldflags = [ - "-Wl,--symbol-ordering-file", - "-Wl,$_rebased_orderfile", - "-Wl,--no-warn-symbol-ordering", - ] - } - inputs = [ - chrome_orderfile, - ] - } -} - # Chrome APK's native library. chrome_common_shared_library("libchrome") { sources = [ @@ -1772,14 +1760,10 @@ { name = "//android_webview:trichrome_webview_apk" is_resource_ids_provider = true - is_compressed_locales_provider = false - is_uncompressed_locales_provider = true }, { name = ":trichrome_chrome_apk" is_resource_ids_provider = false - is_compressed_locales_provider = true - is_uncompressed_locales_provider = false }, ] } @@ -1797,14 +1781,10 @@ { name = "//android_webview:trichrome_webview_for_bundle_apk" is_resource_ids_provider = true - is_compressed_locales_provider = false - is_uncompressed_locales_provider = true }, { name = ":trichrome_chrome_bundle" is_resource_ids_provider = false - is_compressed_locales_provider = true - is_uncompressed_locales_provider = false }, ] }
diff --git a/chrome/android/chrome_common_shared_library.gni b/chrome/android/chrome_common_shared_library.gni index 843ed5c1..a3355585 100644 --- a/chrome/android/chrome_common_shared_library.gni +++ b/chrome/android/chrome_common_shared_library.gni
@@ -14,16 +14,6 @@ bundle_library_suffix = "" apk_pak_asset_type = "_apk" -# This value is set downstream for internal builds. -if (!defined(default_chrome_orderfile)) { - default_chrome_orderfile = "" -} - -declare_args() { - # Path to a linker orderfile to use for libchrome.so, libmonochrome.so, etc. - chrome_orderfile = default_chrome_orderfile -} - # This template contains all common configuration for native shared libraries, # including libchrome, monochrome, standalone webview (also called monochrome), # and libchromefortest (used by chrome_public_test_apk). @@ -71,7 +61,7 @@ deps += [ "//chrome:chrome_android_core" ] } - configs += [ "//chrome/android:orderfile_config" ] + configs += [ "//build/config/compiler:chrome_orderfile_config" ] public_configs = extra_chrome_shared_library_configs deps += extra_chrome_shared_library_deps
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni index 1ebf5160..d16003f7 100644 --- a/chrome/android/chrome_public_apk_tmpl.gni +++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//android_webview/variables.gni") import("//base/android/linker/config.gni") import("//base/android/proguard/proguard.gni") import("//build/config/android/extract_unwind_tables.gni") @@ -157,6 +158,10 @@ "*ic_lock.*", # Bottom edge seems misaligned. ] + if (!_is_monochrome) { + locale_config_java_packages = [ "org.chromium.chrome.browser" ] + } + # Use zh-TW strings for zh-HK (https://crbug.com/780847). if (!defined(support_zh_hk)) { support_zh_hk = true @@ -325,6 +330,10 @@ ] if (is_monochrome) { + locale_config_java_packages = [ + "org.chromium.chrome.browser", + webview_locale_config_java_package, + ] if (android_64bit_target_cpu) { # Build //android_webview:monochrome with the opposite bitness that # Chrome runs in.
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn index 9edcb82..a3f2d624 100644 --- a/chrome/android/features/autofill_assistant/BUILD.gn +++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -155,6 +155,7 @@ java_files = [ "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java", "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java", + "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java", "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java", "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java", "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestTestHelper.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java index eb5e898b..d36c220 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
@@ -9,6 +9,7 @@ import android.animation.ValueAnimator; import android.view.View; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.browser.compositor.animation.CompositorAnimator; import org.chromium.chrome.browser.widget.MaterialProgressBar; @@ -28,6 +29,7 @@ private boolean mIsRunningProgressAnimation; private int mLastProgress; private Queue<ValueAnimator> mPendingIncreaseAnimations = new ArrayDeque<>(); + private int mProgressBarSpeedMs = PROGRESS_BAR_SPEED_MS; AnimatedProgressBar(MaterialProgressBar progressBar) { mProgressBar = progressBar; @@ -51,7 +53,7 @@ } ValueAnimator progressAnimation = ValueAnimator.ofInt(mLastProgress, progress); progressAnimation.setDuration( - PROGRESS_BAR_SPEED_MS * Math.abs(progress - mLastProgress) / 100); + mProgressBarSpeedMs * Math.abs(progress - mLastProgress) / 100); progressAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR); progressAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -75,4 +77,9 @@ progressAnimation.start(); } } + + @VisibleForTesting + void disableAnimationsForTesting(boolean disable) { + mProgressBarSpeedMs = disable ? 0 : PROGRESS_BAR_SPEED_MS; + } }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java index 0313420..502b20f1 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderCoordinator.java
@@ -10,7 +10,9 @@ import android.view.ViewGroup; import android.widget.ImageView; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.autofill_assistant.R; +import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderViewBinder.ViewHolder; import org.chromium.chrome.browser.signin.DisplayableProfileData; import org.chromium.chrome.browser.signin.ProfileDataCache; import org.chromium.components.signin.ChromeSigninController; @@ -26,6 +28,7 @@ private final ViewGroup mView; private final ImageView mProfileView; private final String mSignedInAccountName; + private final ViewHolder mViewHolder; public AssistantHeaderCoordinator(Context context, AssistantHeaderModel model) { // Create the poodle and insert it before the status message. We have to create a view @@ -48,10 +51,9 @@ setupProfileImage(); // Bind view and mediator through the model. - AssistantHeaderViewBinder.ViewHolder viewHolder = - new AssistantHeaderViewBinder.ViewHolder(context, mView, poodle); + mViewHolder = new AssistantHeaderViewBinder.ViewHolder(context, mView, poodle); AssistantHeaderViewBinder viewBinder = new AssistantHeaderViewBinder(); - PropertyModelChangeProcessor.create(model, viewHolder, viewBinder); + PropertyModelChangeProcessor.create(model, mViewHolder, viewBinder); model.set(AssistantHeaderModel.PROGRESS_VISIBLE, true); } @@ -85,16 +87,21 @@ } // TODO(b/130415092): Use image from AGSA if chrome is not signed in. + private void setupProfileImage() { if (mSignedInAccountName != null) { mProfileCache.addObserver(this); mProfileCache.update(Collections.singletonList(mSignedInAccountName)); } } - private void setProfileImageFor(String signedInAccountName) { DisplayableProfileData profileData = mProfileCache.getProfileDataOrDefault(signedInAccountName); mProfileView.setImageDrawable(profileData.getImage()); } + + @VisibleForTesting + public void disableAnimationsForTesting(boolean disable) { + mViewHolder.disableAnimationsForTesting(disable); + } }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java index 4df071e..b0fd9049 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java
@@ -22,7 +22,8 @@ public static final WritableObjectPropertyKey<String> BUBBLE_MESSAGE = new WritableObjectPropertyKey<>(); - static final WritableIntPropertyKey PROGRESS = new WritableIntPropertyKey(); + @VisibleForTesting + public static final WritableIntPropertyKey PROGRESS = new WritableIntPropertyKey(); @VisibleForTesting public static final WritableBooleanPropertyKey PROGRESS_VISIBLE = @@ -30,7 +31,8 @@ static final WritableBooleanPropertyKey SPIN_POODLE = new WritableBooleanPropertyKey(); - static final WritableObjectPropertyKey<Runnable> FEEDBACK_BUTTON_CALLBACK = + @VisibleForTesting + public static final WritableObjectPropertyKey<Runnable> FEEDBACK_BUTTON_CALLBACK = new WritableObjectPropertyKey<>(); public static final WritableObjectPropertyKey<AssistantChip> CHIP =
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java index 630d643..3165381 100644 --- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java +++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -11,6 +11,7 @@ import android.widget.PopupMenu; import android.widget.TextView; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip; import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChipViewHolder; @@ -45,7 +46,7 @@ @Nullable TextBubble mTextBubble; - public ViewHolder(Context context, ViewGroup headerView, AnimatedPoodle poodle) { + ViewHolder(Context context, ViewGroup headerView, AnimatedPoodle poodle) { mContext = context; mPoodle = poodle; mHeader = headerView; @@ -56,6 +57,11 @@ mProfileIconMenu.inflate(R.menu.profile_icon_menu); mProfileIconView.setOnClickListener(unusedView -> mProfileIconMenu.show()); } + + @VisibleForTesting + void disableAnimationsForTesting(boolean disable) { + mProgressBar.disableAnimationsForTesting(disable); + } } @Override
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java index 45b0594a..e5af680 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java
@@ -16,12 +16,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; -import android.support.design.widget.CoordinatorLayout; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.v7.widget.DefaultItemAnimator; -import android.view.Gravity; -import android.view.ViewGroup; import android.widget.LinearLayout; import org.junit.Before; @@ -29,86 +26,71 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantActionsCarouselCoordinator; import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel; import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; -import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content_public.browser.test.util.TestThreadUtils; -import java.util.concurrent.ExecutionException; - /** * Tests for the autofill assistant actions carousel. */ -@RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RunWith(ChromeJUnit4ClassRunner.class) public class AutofillAssistantActionsCarouselUiTest { @Rule - public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); + public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); + + /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */ + private AssistantActionsCarouselCoordinator createCoordinator(AssistantCarouselModel model) + throws Exception { + AssistantActionsCarouselCoordinator coordinator = TestThreadUtils.runOnUiThreadBlocking( + () + -> new AssistantActionsCarouselCoordinator( + InstrumentationRegistry.getTargetContext(), model)); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Note: apparently, we need an intermediate container for this coordinator's view, + // otherwise the view will be invisible. + // @TODO(crbug.com/806868) figure out why this is the case. + LinearLayout container = new LinearLayout(InstrumentationRegistry.getTargetContext()); + container.addView(coordinator.getView()); + AutofillAssistantUiTestUtil.attachToCoordinator(mTestRule.getActivity(), container); + }); + + return coordinator; + } @Before public void setUp() throws Exception { - mCustomTabActivityTestRule.startCustomTabActivityWithIntent( - CustomTabsTestUtils.createMinimalCustomTabIntent( - InstrumentationRegistry.getTargetContext(), "about:blank")); - } - - private CustomTabActivity getActivity() { - return mCustomTabActivityTestRule.getActivity(); - } - - /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */ - private AssistantActionsCarouselCoordinator createCoordinator(AssistantCarouselModel model) { - ThreadUtils.assertOnUiThread(); - AssistantActionsCarouselCoordinator coordinator = new AssistantActionsCarouselCoordinator( - InstrumentationRegistry.getTargetContext(), model); - - // Note: apparently, we need an intermediate container for this coordinator's view, - // otherwise the view will be invisible. - // @TODO(crbug.com/806868) figure out why this is the case. - LinearLayout container = new LinearLayout(InstrumentationRegistry.getTargetContext()); - container.addView(coordinator.getView()); - - CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.BOTTOM; - - ViewGroup chromeCoordinatorView = - getActivity().findViewById(org.chromium.chrome.autofill_assistant.R.id.coordinator); - chromeCoordinatorView.addView(container, lp); - - return coordinator; + AutofillAssistantUiTestUtil.startOnBlankPage(mTestRule); } /** Tests assumptions about the initial state of the carousel. */ @Test @MediumTest - public void testInitialState() { - TestThreadUtils.runOnUiThreadBlocking(() -> { - AssistantCarouselModel model = new AssistantCarouselModel(); - AssistantActionsCarouselCoordinator coordinator = createCoordinator(model); + public void testInitialState() throws Exception { + AssistantCarouselModel model = new AssistantCarouselModel(); + AssistantActionsCarouselCoordinator coordinator = createCoordinator(model); + TestThreadUtils.runOnUiThreadBlocking(() -> { assertThat(((DefaultItemAnimator) coordinator.getView().getItemAnimator()) .getSupportsChangeAnimations(), is(false)); - assertThat(model.getChipsModel().size(), is(0)); - assertThat(coordinator.getView().getAdapter().getItemCount(), is(0)); }); + assertThat(model.getChipsModel().size(), is(0)); + assertThat(coordinator.getView().getAdapter().getItemCount(), is(0)); } /** Adds a single chip and tests assumptions about the view state after the change. */ @Test @MediumTest - public void testAddSingleChip() throws ExecutionException { + public void testAddSingleChip() throws Exception { AssistantCarouselModel model = new AssistantCarouselModel(); - AssistantActionsCarouselCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createCoordinator(model)); + AssistantActionsCarouselCoordinator coordinator = createCoordinator(model); TestThreadUtils.runOnUiThreadBlocking( () @@ -126,10 +108,9 @@ /** Adds multiple chips and tests assumptions about the view state after the change. */ @Test @MediumTest - public void testAddMultipleChips() throws ExecutionException { + public void testAddMultipleChips() throws Exception { AssistantCarouselModel model = new AssistantCarouselModel(); - AssistantActionsCarouselCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createCoordinator(model)); + AssistantActionsCarouselCoordinator coordinator = createCoordinator(model); // Note: this should be a small number that fits on screen without scrolling. int numChips = 3; @@ -154,10 +135,9 @@ /** Adds many chips and tests that the cancel chip is always visible. */ @Test @MediumTest - public void testCancelChipAlwaysVisible() throws ExecutionException { + public void testCancelChipAlwaysVisible() throws Exception { AssistantCarouselModel model = new AssistantCarouselModel(); - AssistantActionsCarouselCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createCoordinator(model)); + AssistantActionsCarouselCoordinator coordinator = createCoordinator(model); // Note: this should be a large number that does not fit on screen without scrolling. int numChips = 30;
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java index b533469..ce5d0c9 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java
@@ -21,12 +21,9 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.design.widget.CoordinatorLayout; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; -import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; @@ -42,18 +39,19 @@ import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetails; import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsCoordinator; import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsModel; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; -import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import java.util.Calendar; import java.util.Locale; /** Tests for the Autofill Assistant details. */ -@RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RunWith(ChromeJUnit4ClassRunner.class) public class AutofillAssistantDetailsUiTest { + @Rule + public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); + private static class ViewHolder { final ImageView mImageView; final TextView mTitleView; @@ -78,20 +76,6 @@ } } - @Rule - public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); - - @Before - public void setUp() throws Exception { - mCustomTabActivityTestRule.startCustomTabActivityWithIntent( - CustomTabsTestUtils.createMinimalCustomTabIntent( - InstrumentationRegistry.getTargetContext(), "about:blank")); - } - - private CustomTabActivity getActivity() { - return mCustomTabActivityTestRule.getActivity(); - } - private AssistantDetailsCoordinator createCoordinator(AssistantDetailsModel model) throws Exception { return createCoordinator(model, Locale.getDefault()); @@ -102,25 +86,24 @@ AssistantDetailsModel model, Locale locale) throws Exception { AssistantDetailsCoordinator coordinator = runOnUiThreadBlocking(() -> { Bitmap testImage = BitmapFactory.decodeResource( - getActivity().getResources(), R.drawable.btn_close); + mTestRule.getActivity().getResources(), R.drawable.btn_close); return new AssistantDetailsCoordinator(InstrumentationRegistry.getTargetContext(), locale, model, new AutofillAssistantUiTestUtil.MockImageFetcher(testImage, null)); }); - runOnUiThreadBlocking(() -> { - CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.BOTTOM; - - ViewGroup chromeCoordinatorView = getActivity().findViewById(R.id.coordinator); - chromeCoordinatorView.addView(coordinator.getView(), lp); - }); - + runOnUiThreadBlocking(() + -> AutofillAssistantUiTestUtil.attachToCoordinator( + mTestRule.getActivity(), coordinator.getView())); return coordinator; } + @Before + public void setUp() throws Exception { + AutofillAssistantUiTestUtil.startOnBlankPage(mTestRule); + } + /** Tests assumptions about the initial state of the details. */ @Test @MediumTest
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java new file mode 100644 index 0000000..098d1f4 --- /dev/null +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java
@@ -0,0 +1,209 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.autofill_assistant; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.PositionAssertions.isRightOf; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.mockito.Mockito.verify; + +import android.support.design.widget.CoordinatorLayout; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.MediumTest; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.autofill_assistant.R; +import org.chromium.chrome.browser.ChromeSwitches; +import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip; +import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip.Icon; +import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderCoordinator; +import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel; +import org.chromium.chrome.browser.customtabs.CustomTabActivity; +import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; +import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; +import org.chromium.chrome.browser.widget.MaterialProgressBar; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +/** + * Tests for the Autofill Assistant header. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class AutofillAssistantHeaderUiTest { + private static class ViewHolder { + private final TextView mStatusMessage; + private final MaterialProgressBar mProgressBar; + private final View mProfileIcon; + + private ViewHolder(View rootView) { + mStatusMessage = rootView.findViewById(R.id.status_message); + mProgressBar = rootView.findViewById(R.id.progress_bar); + mProfileIcon = rootView.findViewById(R.id.profile_image); + } + } + + @Rule + public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); + + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock + public Runnable mRunnableMock; + + @Before + public void setUp() throws Exception { + mCustomTabActivityTestRule.startCustomTabActivityWithIntent( + CustomTabsTestUtils.createMinimalCustomTabIntent( + InstrumentationRegistry.getTargetContext(), "about:blank")); + } + + private CustomTabActivity getActivity() { + return mCustomTabActivityTestRule.getActivity(); + } + + /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */ + private AssistantHeaderCoordinator createCoordinator(AssistantHeaderModel model) { + return TestThreadUtils.runOnUiThreadBlockingNoException(() -> { + AssistantHeaderCoordinator coordinator = + new AssistantHeaderCoordinator(getActivity(), model); + + CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.BOTTOM; + + ViewGroup chromeCoordinatorView = getActivity().findViewById(R.id.coordinator); + chromeCoordinatorView.addView(coordinator.getView(), lp); + coordinator.disableAnimationsForTesting(true); + + return coordinator; + }); + } + + @Test + @MediumTest + public void testInitialState() { + AssistantHeaderModel model = new AssistantHeaderModel(); + AssistantHeaderCoordinator coordinator = createCoordinator(model); + ViewHolder viewHolder = new ViewHolder(coordinator.getView()); + + onView(is(viewHolder.mStatusMessage)) + .check(matches(isDisplayed())) + .check(matches(withText(""))); + + onView(is(viewHolder.mProgressBar)) + .check(matches(isDisplayed())) + .check(matches(hasProgress(0))); + + onView(is(viewHolder.mProfileIcon)).check(matches(isDisplayed())); + } + + @Test + @MediumTest + public void testSimpleModelChanges() { + AssistantHeaderModel model = new AssistantHeaderModel(); + AssistantHeaderCoordinator coordinator = createCoordinator(model); + ViewHolder viewHolder = new ViewHolder(coordinator.getView()); + + String statusMessage = "Hello World"; + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantHeaderModel.STATUS_MESSAGE, statusMessage)); + + onView(is(viewHolder.mStatusMessage)) + .check(matches(isDisplayed())) + .check(matches(withText(statusMessage))); + + int progress = 42; + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantHeaderModel.PROGRESS, progress)); + + onView(is(viewHolder.mProgressBar)) + .check(matches(isDisplayed())) + .check(matches(hasProgress(progress))); + } + + @Test + @MediumTest + public void testChip() { + AssistantHeaderModel model = new AssistantHeaderModel(); + AssistantHeaderCoordinator coordinator = createCoordinator(model); + + String chipText = "Hello World"; + AssistantChip chip = new AssistantChip(AssistantChip.Type.BUTTON_FILLED_BLUE, Icon.DONE, + chipText, /* disabled= */ false, /* sticky= */ false, () -> {}); + + // Set the header chip without displaying it. + TestThreadUtils.runOnUiThreadBlocking(() -> model.set(AssistantHeaderModel.CHIP, chip)); + + Matcher<View> chipMatcher = + allOf(isDescendantOfA(is(coordinator.getView())), withText(chipText)); + onView(chipMatcher).check(matches(not(isDisplayed()))); + + // Show the chip + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantHeaderModel.CHIP_VISIBLE, true)); + onView(chipMatcher) + .check(matches(isDisplayed())) + .check(isRightOf(withId(R.id.status_message))); + } + + @Test + @MediumTest + public void testProfileImageMenu() { + AssistantHeaderModel model = new AssistantHeaderModel(); + AssistantHeaderCoordinator coordinator = createCoordinator(model); + ViewHolder viewHolder = new ViewHolder(coordinator.getView()); + + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK, mRunnableMock)); + + onView(is(viewHolder.mProfileIcon)).perform(click()); + + onView(withText(R.string.autofill_assistant_send_feedback)).perform(click()); + + verify(mRunnableMock).run(); + + // TODO(crbug.com/806868): Test click on the "Settings" menu item. + } + + private static Matcher<View> hasProgress(int expectedProgress) { + return new BaseMatcher<View>() { + @Override + public boolean matches(Object o) { + return ((MaterialProgressBar) o).getProgressForTesting() == expectedProgress; + } + + @Override + public void describeTo(Description description) { + description.appendText("hasProgress: " + expectedProgress); + } + }; + } +}
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java index a61d1ec..3b321ce 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java
@@ -4,19 +4,20 @@ package org.chromium.chrome.browser.autofill_assistant; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withText; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.design.widget.CoordinatorLayout; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; -import android.view.Gravity; -import android.view.ViewGroup; import android.widget.TextView; import org.junit.Before; @@ -24,126 +25,108 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBox; import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxCoordinator; import org.chromium.chrome.browser.autofill_assistant.infobox.AssistantInfoBoxModel; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; -import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content_public.browser.test.util.TestThreadUtils; /** * Tests for the Autofill Assistant infobox. */ -@RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RunWith(ChromeJUnit4ClassRunner.class) public class AutofillAssistantInfoBoxUiTest { @Rule - public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); - - @Before - public void setUp() throws Exception { - mCustomTabActivityTestRule.startCustomTabActivityWithIntent( - CustomTabsTestUtils.createMinimalCustomTabIntent( - InstrumentationRegistry.getTargetContext(), "about:blank")); - } - - private CustomTabActivity getActivity() { - return mCustomTabActivityTestRule.getActivity(); - } + public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); private TextView getExplanationView(AssistantInfoBoxCoordinator coordinator) { return coordinator.getView().findViewById(R.id.info_box_explanation); } /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */ - private AssistantInfoBoxCoordinator createCoordinator(AssistantInfoBoxModel model) { - ThreadUtils.assertOnUiThread(); + private AssistantInfoBoxCoordinator createCoordinator(AssistantInfoBoxModel model) + throws Exception { + AssistantInfoBoxCoordinator coordinator = TestThreadUtils.runOnUiThreadBlocking(() -> { + Bitmap testImage = BitmapFactory.decodeResource( + mTestRule.getActivity().getResources(), R.drawable.btn_close); - Bitmap testImage = - BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.btn_close); + return new AssistantInfoBoxCoordinator(InstrumentationRegistry.getTargetContext(), + model, new AutofillAssistantUiTestUtil.MockImageFetcher(testImage, null)); + }); - AssistantInfoBoxCoordinator coordinator = - new AssistantInfoBoxCoordinator(InstrumentationRegistry.getTargetContext(), model, - new AutofillAssistantUiTestUtil.MockImageFetcher(testImage, null)); - - CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.BOTTOM; - - ViewGroup chromeCoordinatorView = getActivity().findViewById(R.id.coordinator); - chromeCoordinatorView.addView(coordinator.getView(), lp); - + TestThreadUtils.runOnUiThreadBlocking( + () + -> AutofillAssistantUiTestUtil.attachToCoordinator( + mTestRule.getActivity(), coordinator.getView())); return coordinator; } + @Before + public void setUp() throws Exception { + AutofillAssistantUiTestUtil.startOnBlankPage(mTestRule); + } + /** Tests assumptions about the initial state of the infobox. */ @Test @MediumTest - public void testInitialState() { - TestThreadUtils.runOnUiThreadBlocking(() -> { - AssistantInfoBoxModel model = new AssistantInfoBoxModel(); - AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + public void testInitialState() throws Exception { + AssistantInfoBoxModel model = new AssistantInfoBoxModel(); + AssistantInfoBoxCoordinator coordinator = createCoordinator(model); - assertNull(model.get(AssistantInfoBoxModel.INFO_BOX)); - assertFalse(coordinator.getView().isShown()); - }); + assertThat(model.get(AssistantInfoBoxModel.INFO_BOX), nullValue()); + onView(is(coordinator.getView())).check(matches(not(isDisplayed()))); } /** Tests for an infobox with a message, but without an image. */ @Test @MediumTest - public void testMessageNoImage() { - TestThreadUtils.runOnUiThreadBlocking(() -> { - AssistantInfoBoxModel model = new AssistantInfoBoxModel(); - AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + public void testMessageNoImage() throws Exception { + AssistantInfoBoxModel model = new AssistantInfoBoxModel(); + AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + AssistantInfoBox infoBox = new AssistantInfoBox("", "Message"); - AssistantInfoBox infoBox = new AssistantInfoBox("", "Message"); - model.set(AssistantInfoBoxModel.INFO_BOX, infoBox); - - assertTrue(getExplanationView(coordinator).isShown()); - assertNull("Image should not be set", - getExplanationView(coordinator).getCompoundDrawables()[1]); - assertEquals(infoBox.getExplanation(), getExplanationView(coordinator).getText()); - }); + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantInfoBoxModel.INFO_BOX, infoBox)); + onView(is(coordinator.getView())).check(matches(isDisplayed())); + // Image should not be set. + assertThat(getExplanationView(coordinator).getCompoundDrawables()[1], nullValue()); + onView(is(getExplanationView(coordinator))).check(matches(withText("Message"))); } /** Tests for an infobox with message and image. */ @Test @MediumTest - public void testImage() { - TestThreadUtils.runOnUiThreadBlocking(() -> { - AssistantInfoBoxModel model = new AssistantInfoBoxModel(); - AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + public void testImage() throws Exception { + AssistantInfoBoxModel model = new AssistantInfoBoxModel(); + AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + AssistantInfoBox infoBox = new AssistantInfoBox("x", "Message"); - AssistantInfoBox infoBox = new AssistantInfoBox("x", "Message"); - model.set(AssistantInfoBoxModel.INFO_BOX, infoBox); - - assertTrue(getExplanationView(coordinator).isShown()); - assertNotNull("Image should be set", - getExplanationView(coordinator).getCompoundDrawables()[1]); - assertEquals(infoBox.getExplanation(), getExplanationView(coordinator).getText()); - }); + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantInfoBoxModel.INFO_BOX, infoBox)); + onView(is(getExplanationView(coordinator))).check(matches(isDisplayed())); + // Image should be set. + assertThat(getExplanationView(coordinator).getCompoundDrawables()[1], not(nullValue())); + onView(is(getExplanationView(coordinator))).check(matches(withText("Message"))); } @Test @MediumTest - public void testVisibility() { - TestThreadUtils.runOnUiThreadBlocking(() -> { - AssistantInfoBoxModel model = new AssistantInfoBoxModel(); - AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + public void testVisibility() throws Exception { + AssistantInfoBoxModel model = new AssistantInfoBoxModel(); + AssistantInfoBoxCoordinator coordinator = createCoordinator(model); + AssistantInfoBox infoBox = new AssistantInfoBox("", ""); - AssistantInfoBox infoBox = new AssistantInfoBox("", ""); - model.set(AssistantInfoBoxModel.INFO_BOX, infoBox); - assertTrue(coordinator.getView().isShown()); + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantInfoBoxModel.INFO_BOX, infoBox)); + onView(is(coordinator.getView())).check(matches(isDisplayed())); - model.set(AssistantInfoBoxModel.INFO_BOX, null); - assertFalse(coordinator.getView().isShown()); - }); + TestThreadUtils.runOnUiThreadBlocking( + () -> model.set(AssistantInfoBoxModel.INFO_BOX, null)); + onView(is(coordinator.getView())).check(matches(not(isDisplayed()))); } }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java index 89926b4..0079e50 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java
@@ -22,7 +22,6 @@ import android.util.DisplayMetrics; import org.json.JSONArray; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -33,14 +32,12 @@ import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator; import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel; import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; import org.chromium.content_public.browser.test.util.TestTouchUtils; -import org.chromium.net.test.EmbeddedTestServer; import java.util.Collections; import java.util.concurrent.ExecutionException; @@ -48,46 +45,33 @@ /** * Tests for the Autofill Assistant overlay. */ -@RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RunWith(ChromeJUnit4ClassRunner.class) public class AutofillAssistantOverlayUiTest { + @Rule + public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); + // TODO(crbug.com/806868): Create a more specific test site for overlay testing. private static final String TEST_PAGE = "/components/test/data/autofill_assistant/autofill_assistant_target_website.html"; - private EmbeddedTestServer mTestServer; - - @Rule - public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); - @Before public void setUp() throws Exception { - mTestServer = EmbeddedTestServer.createAndStartServer( - InstrumentationRegistry.getTargetContext().getApplicationContext()); - String testPage = mTestServer.getURL(TEST_PAGE); - mCustomTabActivityTestRule.startCustomTabActivityWithIntent( - CustomTabsTestUtils.createMinimalCustomTabIntent( - InstrumentationRegistry.getTargetContext(), testPage)); - mCustomTabActivityTestRule.getActivity().getScrim().disableAnimationForTesting(true); - } - - @After - public void tearDown() throws Exception { - mTestServer.stopAndDestroyServer(); - } - - private CustomTabActivity getActivity() { - return mCustomTabActivityTestRule.getActivity(); + mTestRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent( + InstrumentationRegistry.getTargetContext(), + mTestRule.getTestServer().getURL(TEST_PAGE))); + mTestRule.getActivity().getScrim().disableAnimationForTesting(true); } private WebContents getWebContents() { - return mCustomTabActivityTestRule.getWebContents(); + return mTestRule.getWebContents(); } /** Creates a coordinator for use in UI tests. */ private AssistantOverlayCoordinator createCoordinator(AssistantOverlayModel model) throws ExecutionException { - return runOnUiThreadBlocking(() -> new AssistantOverlayCoordinator(getActivity(), model)); + return runOnUiThreadBlocking( + () -> new AssistantOverlayCoordinator(mTestRule.getActivity(), model)); } /** Tests assumptions about the initial state of the infobox. */ @@ -190,15 +174,15 @@ // The scrim view is only attached to the view hierarchy when needed, preventing us from // using regular espresso facilities. boolean scrimInHierarchy = - runOnUiThreadBlocking(() -> getActivity().getScrim().getParent() != null); + runOnUiThreadBlocking(() -> mTestRule.getActivity().getScrim().getParent() != null); if (expected && !scrimInHierarchy) { throw new Exception("Expected scrim view visible, but scrim was not in view hierarchy"); } if (scrimInHierarchy) { if (expected) { - onView(is(getActivity().getScrim())).check(matches(isDisplayed())); + onView(is(mTestRule.getActivity().getScrim())).check(matches(isDisplayed())); } else { - onView(is(getActivity().getScrim())).check(matches(not(isDisplayed()))); + onView(is(mTestRule.getActivity().getScrim())).check(matches(not(isDisplayed()))); } } } @@ -210,7 +194,7 @@ float y = coords.top + 0.5f * (coords.bottom - coords.top); // Sanity check, can only click on coordinates on screen. - DisplayMetrics displayMetrics = getActivity().getResources().getDisplayMetrics(); + DisplayMetrics displayMetrics = mTestRule.getActivity().getResources().getDisplayMetrics(); if (x < 0 || x > displayMetrics.widthPixels || y < 0 || y > displayMetrics.heightPixels) { throw new IllegalArgumentException(elementId + " not on screen: tried to tap x=" + x + ", y=" + y + ", which is outside of display with w=" @@ -230,13 +214,14 @@ * - Then, convert compositor space to screen space (add content offset). */ Rect viewport = getViewport(); - float cssToPysicalPixels = (((float) getActivity().getCompositorViewHolder().getWidth() - / (float) viewport.width())); + float cssToPysicalPixels = + (((float) mTestRule.getActivity().getCompositorViewHolder().getWidth() + / (float) viewport.width())); int[] compositorLocation = new int[2]; - getActivity().getCompositorViewHolder().getLocationOnScreen(compositorLocation); - int offsetY = - compositorLocation[1] + getActivity().getFullscreenManager().getContentOffset(); + mTestRule.getActivity().getCompositorViewHolder().getLocationOnScreen(compositorLocation); + int offsetY = compositorLocation[1] + + mTestRule.getActivity().getFullscreenManager().getContentOffset(); return new Rect((int) ((elementRect.left - viewport.left) * cssToPysicalPixels), (int) ((elementRect.top - viewport.top) * cssToPysicalPixels + offsetY), (int) ((elementRect.right - viewport.left) * cssToPysicalPixels),
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java index 345005b3..f9b6b41 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequestUiTest.java
@@ -24,19 +24,14 @@ import static org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting.VERTICAL_EXPANDER_CHEVRON; -import android.support.design.widget.CoordinatorLayout; -import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; -import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.browser.ChromeSwitches; @@ -46,54 +41,37 @@ import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestCoordinator; import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestModel; import org.chromium.chrome.browser.autofill_assistant.payment.AssistantTermsAndConditionsState; -import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; -import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.test.util.TestThreadUtils; /** * Tests for the Autofill Assistant payment request UI. */ -@RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +@RunWith(ChromeJUnit4ClassRunner.class) public class AutofillAssistantPaymentRequestUiTest { - private AutofillAssistantPaymentRequestTestHelper mHelper; - @Rule - public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule(); + public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule(); + + private AutofillAssistantPaymentRequestTestHelper mHelper; @Before public void setUp() throws Exception { - mCustomTabActivityTestRule.startCustomTabActivityWithIntent( - CustomTabsTestUtils.createMinimalCustomTabIntent( - InstrumentationRegistry.getTargetContext(), "about:blank")); + AutofillAssistantUiTestUtil.startOnBlankPage(mTestRule); mHelper = new AutofillAssistantPaymentRequestTestHelper(); } - private CustomTabActivity getActivity() { - return mCustomTabActivityTestRule.getActivity(); - } - - private WebContents getWebContents() { - return mCustomTabActivityTestRule.getWebContents(); - } - /** Creates a coordinator for use in UI tests, and adds it to the global view hierarchy. */ private AssistantPaymentRequestCoordinator createPaymentRequestCoordinator( - AssistantPaymentRequestModel model) { - ThreadUtils.assertOnUiThread(); - AssistantPaymentRequestCoordinator coordinator = - new AssistantPaymentRequestCoordinator(getActivity(), model); + AssistantPaymentRequestModel model) throws Exception { + AssistantPaymentRequestCoordinator coordinator = TestThreadUtils.runOnUiThreadBlocking( + () -> new AssistantPaymentRequestCoordinator(mTestRule.getActivity(), model)); - CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.BOTTOM; - - ViewGroup chromeCoordinatorView = getActivity().findViewById(R.id.coordinator); - chromeCoordinatorView.addView(coordinator.getView(), lp); - + TestThreadUtils.runOnUiThreadBlocking( + () + -> AutofillAssistantUiTestUtil.attachToCoordinator( + mTestRule.getActivity(), coordinator.getView())); return coordinator; } @@ -104,8 +82,7 @@ @MediumTest public void testInitialState() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); /* Test initial model state. */ assertThat(model.get(AssistantPaymentRequestModel.VISIBLE), is(false)); @@ -153,8 +130,7 @@ @MediumTest public void testSectionVisibility() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking( () -> new AutofillAssistantPaymentRequestTestHelper.ViewHolder(coordinator)); @@ -221,8 +197,7 @@ @MediumTest public void testEmptyPaymentRequest() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper.MockDelegate delegate = new AutofillAssistantPaymentRequestTestHelper.MockDelegate(); AutofillAssistantPaymentRequestTestHelper @@ -301,15 +276,14 @@ @MediumTest public void testContactDetailsLiveUpdate() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking( () -> new AutofillAssistantPaymentRequestTestHelper.ViewHolder(coordinator)); TestThreadUtils.runOnUiThreadBlocking(() -> { // WEB_CONTENTS are necessary for the creation of the editors. - model.set(AssistantPaymentRequestModel.WEB_CONTENTS, getWebContents()); + model.set(AssistantPaymentRequestModel.WEB_CONTENTS, mTestRule.getWebContents()); model.set(AssistantPaymentRequestModel.REQUEST_NAME, true); model.set(AssistantPaymentRequestModel.REQUEST_EMAIL, true); model.set(AssistantPaymentRequestModel.VISIBLE, true); @@ -357,15 +331,14 @@ @MediumTest public void testPaymentMethodsLiveUpdate() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper .ViewHolder viewHolder = TestThreadUtils.runOnUiThreadBlocking( () -> new AutofillAssistantPaymentRequestTestHelper.ViewHolder(coordinator)); TestThreadUtils.runOnUiThreadBlocking(() -> { // WEB_CONTENTS are necessary for the creation of the editors. - model.set(AssistantPaymentRequestModel.WEB_CONTENTS, getWebContents()); + model.set(AssistantPaymentRequestModel.WEB_CONTENTS, mTestRule.getWebContents()); model.set(AssistantPaymentRequestModel.REQUEST_PAYMENT, true); model.set(AssistantPaymentRequestModel.VISIBLE, true); }); @@ -424,8 +397,7 @@ mHelper.setCreditCard(creditCard); AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper.MockDelegate delegate = new AutofillAssistantPaymentRequestTestHelper.MockDelegate(); AutofillAssistantPaymentRequestTestHelper @@ -513,8 +485,7 @@ @MediumTest public void testRemoveLastItemImplicitSelection() throws Exception { AssistantPaymentRequestModel model = new AssistantPaymentRequestModel(); - AssistantPaymentRequestCoordinator coordinator = - TestThreadUtils.runOnUiThreadBlocking(() -> createPaymentRequestCoordinator(model)); + AssistantPaymentRequestCoordinator coordinator = createPaymentRequestCoordinator(model); AutofillAssistantPaymentRequestTestHelper.MockDelegate delegate = new AutofillAssistantPaymentRequestTestHelper.MockDelegate(); AutofillAssistantPaymentRequestTestHelper
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java index be701fdb..45d18ee 100644 --- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java +++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java
@@ -6,13 +6,21 @@ import android.graphics.Bitmap; import android.support.annotation.Nullable; +import android.support.design.widget.CoordinatorLayout; +import android.support.test.InstrumentationRegistry; +import android.view.Gravity; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.chromium.base.Callback; +import org.chromium.base.ThreadUtils; +import org.chromium.chrome.browser.customtabs.CustomTabActivity; +import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; +import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils; import org.chromium.chrome.browser.image_fetcher.ImageFetcher; import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig; @@ -69,4 +77,26 @@ } }; } + + /** + * Attaches the specified view to the Chrome coordinator. Must be called from the UI thread. + */ + public static void attachToCoordinator(CustomTabActivity activity, View view) { + ThreadUtils.assertOnUiThread(); + ViewGroup chromeCoordinatorView = + activity.findViewById(org.chromium.chrome.autofill_assistant.R.id.coordinator); + CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.BOTTOM; + chromeCoordinatorView.addView(view, lp); + } + + /** + * Starts the CCT test rule on a blank page. + */ + public static void startOnBlankPage(CustomTabActivityTestRule testRule) + throws InterruptedException { + testRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent( + InstrumentationRegistry.getTargetContext(), "about:blank")); + } }
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java index e093034..eeecfa9 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/AutofillKeyboardAccessoryIntegrationTest.java
@@ -18,11 +18,10 @@ import static org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabTestHelper.isKeyboardAccessoryTabLayout; import android.app.Activity; +import android.os.Build; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; -import android.support.v7.widget.RecyclerView; import android.view.View; -import android.view.ViewGroup; import org.junit.Rule; import org.junit.Test; @@ -30,6 +29,7 @@ import org.chromium.autofill.mojom.FocusedFieldType; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; @@ -50,6 +50,7 @@ * Integration tests for autofill keyboard accessory. */ @RunWith(ChromeJUnit4ClassRunner.class) +@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/958631") @RetryOnFailure @EnableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY}) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @@ -117,19 +118,19 @@ throws ExecutionException, InterruptedException, TimeoutException { loadTestPage(FakeKeyboard::new); mHelper.clickNodeAndShowKeyboard("EMAIL_ADDRESS"); - mHelper.waitForKeyboardAccessoryToBeShown(); - CriteriaHelper.pollUiThread(() -> getFirstSuggestion() != null); // Wait for suggestions. + mHelper.waitForKeyboardAccessoryToBeShown(true); // Scroll to the second position and check it actually happened. - TestThreadUtils.runOnUiThreadBlocking(() -> getSuggestionsComponent().scrollToPosition(2)); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mHelper.getAccessoryBarView().scrollToPosition(2); }); CriteriaHelper.pollUiThread(() -> { - return getSuggestionsComponent().computeHorizontalScrollOffset() > 0; + return mHelper.getAccessoryBarView().computeHorizontalScrollOffset() > 0; }, "Should keep the manual scroll position."); // Clicking any other node should now scroll the items back to the initial position. mHelper.clickNodeAndShowKeyboard("NAME_LAST"); CriteriaHelper.pollUiThread(() -> { - return getSuggestionsComponent().computeHorizontalScrollOffset() == 0; + return mHelper.getAccessoryBarView().computeHorizontalScrollOffset() == 0; }, "Should be scrolled back to position 0."); } @@ -143,10 +144,10 @@ throws ExecutionException, InterruptedException, TimeoutException { loadTestPage(FakeKeyboard::new); mHelper.clickNodeAndShowKeyboard("NAME_FIRST"); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); - CriteriaHelper.pollUiThread(() -> getFirstSuggestion() != null); - TestThreadUtils.runOnUiThreadBlocking(() -> getFirstSuggestion().performClick()); + TestThreadUtils.runOnUiThreadBlocking( + () -> mHelper.getFirstAccessorySuggestion().performClick()); mHelper.waitForKeyboardAccessoryToDisappear(); } @@ -157,10 +158,10 @@ MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true); loadTestPage(MultiWindowKeyboard::new); mHelper.clickNode("NAME_FIRST", FocusedFieldType.FILLABLE_NON_SEARCH_FIELD); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); - CriteriaHelper.pollUiThread(() -> getFirstSuggestion() != null); - TestThreadUtils.runOnUiThreadBlocking(() -> getFirstSuggestion().performClick()); + TestThreadUtils.runOnUiThreadBlocking( + () -> mHelper.getFirstAccessorySuggestion().performClick()); mHelper.waitForKeyboardAccessoryToDisappear(); } @@ -170,7 +171,7 @@ throws InterruptedException, TimeoutException, ExecutionException { loadTestPage(MultiWindowKeyboard::new); mHelper.clickNodeAndShowKeyboard("NAME_FIRST"); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); whenDisplayed(withId(R.id.bar_items_view)) .perform(scrollTo(isKeyboardAccessoryTabLayout())) @@ -191,7 +192,7 @@ MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true); loadTestPage(MultiWindowKeyboard::new); mHelper.clickNode("NAME_FIRST", FocusedFieldType.FILLABLE_NON_SEARCH_FIELD); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); whenDisplayed(withId(R.id.bar_items_view)) .perform(scrollTo(isKeyboardAccessoryTabLayout()), @@ -213,18 +214,4 @@ return sheetView.getHeight() == 0 || !sheetView.isShown(); }); } - - private RecyclerView getSuggestionsComponent() { - final ViewGroup keyboardAccessory = TestThreadUtils.runOnUiThreadBlockingNoException( - () -> mActivityTestRule.getActivity().findViewById(R.id.keyboard_accessory)); - assert keyboardAccessory != null; - return (RecyclerView) keyboardAccessory.findViewById(R.id.bar_items_view); - } - - private View getFirstSuggestion() { - ViewGroup recyclerView = getSuggestionsComponent(); - assert recyclerView != null; - View view = recyclerView.getChildAt(0); - return isKeyboardAccessoryTabLayout().matches(view) ? null : view; - } }
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java index 8f59c0c..66e5a29 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
@@ -12,6 +12,7 @@ import static org.hamcrest.core.AllOf.allOf; import static org.chromium.autofill.mojom.FocusedFieldType.FILLABLE_NON_SEARCH_FIELD; +import static org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabTestHelper.isKeyboardAccessoryTabLayout; import static org.chromium.chrome.test.util.ViewUtils.VIEW_GONE; import static org.chromium.chrome.test.util.ViewUtils.VIEW_INVISIBLE; import static org.chromium.chrome.test.util.ViewUtils.VIEW_NULL; @@ -141,6 +142,20 @@ .getManualFillingComponent(); } + public RecyclerView getAccessoryBarView() { + final ViewGroup keyboardAccessory = TestThreadUtils.runOnUiThreadBlockingNoException( + () -> mActivityTestRule.getActivity().findViewById(R.id.keyboard_accessory)); + assert keyboardAccessory != null; + return (RecyclerView) keyboardAccessory.findViewById(R.id.bar_items_view); + } + + public View getFirstAccessorySuggestion() { + ViewGroup recyclerView = getAccessoryBarView(); + assert recyclerView != null; + View view = recyclerView.getChildAt(0); + return isKeyboardAccessoryTabLayout().matches(view) ? null : view; + } + public void focusPasswordField() throws TimeoutException, InterruptedException { DOMUtils.focusNode(mActivityTestRule.getWebContents(), PASSWORD_NODE_ID); TestThreadUtils.runOnUiThreadBlocking( @@ -222,6 +237,10 @@ } public void waitForKeyboardAccessoryToBeShown() { + waitForKeyboardAccessoryToBeShown(false); + } + + public void waitForKeyboardAccessoryToBeShown(boolean waitForSuggestionsToLoad) { CriteriaHelper.pollInstrumentationThread(() -> { KeyboardAccessoryCoordinator accessory = getManualFillingCoordinator().getMediatorForTesting().getKeyboardAccessory(); @@ -231,6 +250,11 @@ View accessory = mActivityTestRule.getActivity().findViewById(R.id.keyboard_accessory); return accessory != null && accessory.isShown(); }); + if (waitForSuggestionsToLoad) { + CriteriaHelper.pollUiThread(() + -> getFirstAccessorySuggestion() != null, + "Waited for suggestions that never appeared."); + } } public DropdownPopupWindowInterface waitForAutofillPopup(String filterInput) {
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java index ed7195a..63ce6aed2 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/AddressAccessoryIntegrationTest.java
@@ -23,6 +23,7 @@ import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.whenDisplayed; import static org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabTestHelper.isKeyboardAccessoryTabLayout; +import android.os.Build; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; import android.widget.TextView; @@ -34,6 +35,7 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; @@ -50,13 +52,13 @@ import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.DOMUtils; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** * Integration tests for address accessory views. */ @RunWith(ChromeJUnit4ClassRunner.class) +@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/958631") @RetryOnFailure @EnableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY}) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @@ -112,7 +114,7 @@ // Focus the field to bring up the accessory. mHelper.focusPasswordField(); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); // Click the tab to show the sheet and hide the keyboard. whenDisplayed(allOf(withContentDescription(R.string.address_accessory_sheet_toggle), @@ -126,11 +128,10 @@ @Test @MediumTest @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID}) - public void testFillsSuggestionOnClick() - throws ExecutionException, InterruptedException, TimeoutException { + public void testFillsSuggestionOnClick() throws InterruptedException, TimeoutException { loadTestPage(FakeKeyboard::new); mHelper.clickNodeAndShowKeyboard("NAME_FIRST"); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); // Scroll to last element and click the second icon: whenDisplayed(withId(R.id.bar_items_view))
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java index 258648cb..0dc6ce19 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/CreditCardAccessoryIntegrationTest.java
@@ -23,6 +23,7 @@ import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.whenDisplayed; import static org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabTestHelper.isKeyboardAccessoryTabLayout; +import android.os.Build; import android.support.test.filters.MediumTest; import android.support.test.filters.SmallTest; import android.widget.TextView; @@ -34,6 +35,7 @@ import org.junit.runner.RunWith; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; @@ -50,7 +52,6 @@ import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.DOMUtils; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; /** @@ -58,6 +59,7 @@ */ @RunWith(ChromeJUnit4ClassRunner.class) +@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/958631") @RetryOnFailure @EnableFeatures({ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY}) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @@ -117,7 +119,7 @@ // Focus the field to bring up the accessory. mHelper.focusPasswordField(); - mHelper.waitForKeyboardAccessoryToBeShown(); + mHelper.waitForKeyboardAccessoryToBeShown(true); // Click the tab to show the sheet and hide the keyboard. whenDisplayed(allOf(withContentDescription(R.string.credit_card_accessory_sheet_toggle), @@ -131,12 +133,11 @@ @Test @MediumTest @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID}) - public void testFillsSuggestionOnClick() - throws ExecutionException, InterruptedException, TimeoutException { + public void testFillsSuggestionOnClick() throws InterruptedException, TimeoutException { loadTestPage(FakeKeyboard::new); mHelper.clickNodeAndShowKeyboard("CREDIT_CARD_NAME_FULL"); - mHelper.waitForKeyboardAccessoryToBeShown(); DOMUtils.focusNode(mActivityTestRule.getWebContents(), "CREDIT_CARD_NAME_FULL"); + mHelper.waitForKeyboardAccessoryToBeShown(true); // Scroll to last element and click the second icon: whenDisplayed(withId(R.id.bar_items_view))
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java index a2b3c2b7..c7182c0 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
@@ -42,9 +42,11 @@ * Integration tests for password accessory views. */ @RunWith(ChromeJUnit4ClassRunner.class) -@RetryOnFailure -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) -public class PasswordAccessoryIntegrationTest { +@DisableIf + .Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/958631") + @RetryOnFailure + @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) + public class PasswordAccessoryIntegrationTest { @Rule public final ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); @@ -101,7 +103,6 @@ @Test @SmallTest - @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/958631") public void testFillsPasswordOnTap() throws InterruptedException, TimeoutException { mHelper.loadTestPage(false); mHelper.cacheCredentials("mpark@abc.com", "ShorterPassword");
diff --git a/chrome/android/features/vr/java/AndroidManifest.xml b/chrome/android/features/vr/java/AndroidManifest.xml index 4a2c233..388353f7 100644 --- a/chrome/android/features/vr/java/AndroidManifest.xml +++ b/chrome/android/features/vr/java/AndroidManifest.xml
@@ -8,9 +8,17 @@ featureSplit="vr"> <dist:module - dist:onDemand="true" - dist:title="@string/vr_module_title"> - <dist:fusing dist:include="false" /> + dist:instant="false" + dist:title="@string/vr_module_title"> + <dist:fusing dist:include="false" /> + <dist:delivery> + <dist:install-time> + <dist:conditions> + <dist:device-feature dist:name="android.hardware.vr.high_performance" /> + </dist:conditions> + </dist:install-time> + <dist:on-demand /> + </dist:delivery> </dist:module> <application>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java index acda95a..a9d3e98 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.share.ShareHelper; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.util.FeatureUtilities; +import org.chromium.ui.base.ResourceBundle; import java.util.Locale; @@ -261,11 +262,21 @@ String systemLanguage = LocaleUtils.toLanguage(LocaleUtils.toLanguageTag(Locale.getDefault())); boolean isWrongLanguage = !systemLanguage.equals(uiLanguage) - && LocaleUtils.isLanguageSupported(systemLanguage); + && isLanguageSupported( + systemLanguage, ResourceBundle.getAvailableCompressedPakLocales()); RecordHistogram.recordBooleanHistogram( "Android.Language.WrongLanguageAfterResume", isWrongLanguage); } + private static boolean isLanguageSupported(String language, String[] compressedLocales) { + for (String languageTag : compressedLocales) { + if (LocaleUtils.toLanguage(languageTag).equals(language)) { + return true; + } + } + return false; + } + /** * @return The PowerBroadcastReceiver for the browser process. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java index af23a70..cf2a02f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -49,6 +49,7 @@ import org.chromium.chrome.browser.vr.VrModuleProvider; import org.chromium.components.embedder_support.application.FontPreloadingWorkaround; import org.chromium.components.module_installer.ModuleInstaller; +import org.chromium.ui.base.ResourceBundle; /** * Basic application functionality that should be shared among all browser applications that use @@ -140,6 +141,8 @@ } AsyncTask.takeOverAndroidThreadPool(); JNIUtils.setClassLoader(getClassLoader()); + ResourceBundle.setAvailablePakLocales( + LocaleConfig.COMPRESSED_LOCALES, LocaleConfig.UNCOMPRESSED_LOCALES); if (isBrowserProcess) { TraceEvent.end("ChromeApplication.attachBaseContext");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index 72e2018..2de2b71 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -286,6 +286,7 @@ public static final String OMNIBOX_SPARE_RENDERER = "OmniboxSpareRenderer"; public static final String OVERLAY_NEW_LAYOUT = "OverlayNewLayout"; public static final String OVERSCROLL_HISTORY_NAVIGATION = "OverscrollHistoryNavigation"; + public static final String PASSWORD_EDITING_ANDROID = "PasswordEditingAndroid"; public static final String PAY_WITH_GOOGLE_V1 = "PayWithGoogleV1"; public static final String PERMISSION_DELEGATION = "PermissionDelegation"; public static final String PER_METHOD_CAN_MAKE_PAYMENT_QUOTA =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DeviceItemAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/DeviceItemAdapter.java index c69d333..ebd7bd1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DeviceItemAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/DeviceItemAdapter.java
@@ -23,9 +23,7 @@ import org.chromium.chrome.R; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; /** * An adapter for keeping track of which items to show in the dialog. @@ -70,9 +68,6 @@ // or -1 (INVALID_POSITION) if nothing is selected. private int mSelectedItem = ListView.INVALID_POSITION; - // A set of keys that are marked as disabled in the dialog. - private Set<String> mDisabledEntries = new HashSet<String>(); - // Item descriptions are counted in a map. private Map<String, Integer> mItemDescriptionMap = new HashMap<>(); @@ -105,7 +100,6 @@ boolean isEmpty = super.isEmpty(); if (isEmpty) { assert mKeyToItemMap.isEmpty(); - assert mDisabledEntries.isEmpty(); assert mItemDescriptionMap.isEmpty(); } else { assert !mKeyToItemMap.isEmpty(); @@ -175,7 +169,6 @@ @Override public void clear() { mKeyToItemMap.clear(); - mDisabledEntries.clear(); mItemDescriptionMap.clear(); updateSelectedItemPosition(ListView.INVALID_POSITION); super.clear(); @@ -207,28 +200,6 @@ } /** - * Sets whether the item is enabled. Disabled items are grayed out. - * @param id The id of the item to affect. - * @param enabled Whether the item should be enabled or not. - */ - public void setEnabled(String id, boolean enabled) { - if (enabled) { - mDisabledEntries.remove(id); - } else { - mDisabledEntries.add(id); - } - - if (mSelectedItem != ListView.INVALID_POSITION) { - DeviceItemRow selectedRow = getItem(mSelectedItem); - if (id.equals(selectedRow.mKey) && !enabled) { - updateSelectedItemPosition(ListView.INVALID_POSITION); - } - } - - notifyDataSetChanged(); - } - - /** * Sets the observer to be notified of the item selection change in the adapter. * @param observer The observer to be notified. */ @@ -238,9 +209,7 @@ @Override public boolean isEnabled(int position) { - if (!mItemsSelectable) return false; - DeviceItemRow item = getItem(position); - return !mDisabledEntries.contains(item.mKey); + return mItemsSelectable; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java index 3b5ded4..4ff29f0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java
@@ -315,15 +315,6 @@ } /** - * Sets whether the item is enabled. - * @param key Unique indetifier for the item. - * @param enabled Whether the item should be enabled or not. - */ - public void setEnabled(String key, boolean enabled) { - mItemAdapter.setEnabled(key, enabled); - } - - /** * Indicates the adapter is being initialized. */ public void signalInitializingAdapter() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java index 2353e15..06507fc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfileEditorPreference.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.preferences.autofill; import android.app.Activity; +import android.content.Context; import android.os.Bundle; import android.support.v7.preference.Preference; @@ -27,9 +28,10 @@ private AutofillAddress mAutofillAddress; private String mGUID; + // TODO(crbug.com/982338): Remove Activity usage for Support Library migration. public AutofillProfileEditorPreference( - Activity activity, EditorObserverForTest observerForTest) { - super(activity); + Activity activity, Context styledContext, EditorObserverForTest observerForTest) { + super(styledContext); mActivity = activity; mObserverForTest = observerForTest; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java index ab16059..9e4c5c9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java
@@ -92,14 +92,14 @@ // Add a preference for the profile. Preference pref; if (profile.getIsLocal()) { - AutofillProfileEditorPreference localPref = - new AutofillProfileEditorPreference(getActivity(), sObserverForTest); + AutofillProfileEditorPreference localPref = new AutofillProfileEditorPreference( + getActivity(), getStyledContext(), sObserverForTest); localPref.setTitle(profile.getFullName()); localPref.setSummary(profile.getLabel()); localPref.setKey(localPref.getTitle().toString()); // For testing. pref = localPref; } else { - pref = new Preference(getActivity()); + pref = new Preference(getStyledContext()); pref.setWidgetLayoutResource(R.layout.autofill_server_data_label); pref.setFragment(AutofillServerProfilePreferences.class.getName()); } @@ -113,8 +113,8 @@ // Add 'Add address' button. Tap of it brings up address editor which allows users type in // new addresses. if (PersonalDataManager.isAutofillProfileEnabled()) { - AutofillProfileEditorPreference pref = - new AutofillProfileEditorPreference(getActivity(), sObserverForTest); + AutofillProfileEditorPreference pref = new AutofillProfileEditorPreference( + getActivity(), getStyledContext(), sObserverForTest); Drawable plusIcon = ApiCompatibilityUtils.getDrawable(getResources(), R.drawable.plus); plusIcon.mutate(); plusIcon.setColorFilter(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java index 10ba3ea2..5947079 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/password/SavePasswordsPreferences.java
@@ -22,6 +22,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.StrictModeContext; @@ -131,6 +132,14 @@ } @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // Disable animations of preference changes. + getListView().setItemAnimator(null); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); mMenu = menu; @@ -394,7 +403,7 @@ mSavePasswordsSwitch.setManagedPreferenceDelegate( preference -> PrefServiceBridge.getInstance().isRememberPasswordsManaged()); - try (StrictModeContext ctx = StrictModeContext.allowDiskReads()) { + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { getPreferenceScreen().addPreference(mSavePasswordsSwitch); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java index 65f1d73..e642554 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ClearBrowsingDataPreferences.java
@@ -598,6 +598,9 @@ clearButton.setOnClickListener((View v) -> onClearButtonClicked()); view.addView(clearButton); + // Disable animations of preference changes. + getListView().setItemAnimator(null); + return view; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java index 0d79d7d..853da20 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
@@ -98,6 +98,10 @@ if (FeatureUtilities.isNoTouchModeEnabled()) { getPreferenceScreen().removePreference(findPreference(Type.CLIPBOARD)); } + CommandLine commandLine = CommandLine.getInstance(); + if (!commandLine.hasSwitch(ContentSwitches.ENABLE_WEB_BLUETOOTH_SCANNING)) { + getPreferenceScreen().removePreference(findPreference(Type.BLUETOOTH_SCANNING)); + } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java index 103d0fd..fd04334 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabWindowManager.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.tabmodel; import android.app.Activity; +import android.os.Build; import android.util.SparseArray; import org.chromium.base.ActivityState; @@ -12,8 +13,8 @@ import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.ThreadUtils; import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.ui.base.WindowAndroid; import java.util.ArrayList; @@ -207,11 +208,12 @@ @Override public TabModelSelector buildSelector(Activity activity, TabCreatorManager tabCreatorManager, int selectorIndex) { - // Merge tabs if this is the TabModelSelector for ChromeTabbedActivity and there are no - // other instances running. This indicates that it is a complete cold start of - // ChromeTabbedActivity. Tabs should only be merged during a cold start of - // ChromeTabbedActivity and not other instances (e.g. ChromeTabbedActivity2). - boolean mergeTabs = activity.getClass().equals(ChromeTabbedActivity.class) + // Merge tabs if this TabModelSelector is for a ChromeTabbedActivity created in + // fullscreen mode and there are no TabModelSelector's currently alive. This indicates + // that it is a cold start or process restart in fullscreen mode. + boolean mergeTabs = Build.VERSION.SDK_INT > Build.VERSION_CODES.M + && FeatureUtilities.isTabModelMergingEnabled() + && !activity.isInMultiWindowMode() && getInstance().getNumberOfAssignedTabModelSelectors() == 0; TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy( selectorIndex, mergeTabs);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java index 5b7b56d..25e35e1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/MaterialProgressBar.java
@@ -16,6 +16,7 @@ import android.view.View; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; /** @@ -240,4 +241,10 @@ canvas.drawRect(start, 0, end, canvas.getHeight(), paint); } } + + /** @return The current progress value. */ + @VisibleForTesting + public int getProgressForTesting() { + return mProgress; + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java index 4ea6f4b2..6dafc3af 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ItemChooserDialogTest.java
@@ -549,83 +549,6 @@ @Test @LargeTest - public void testDisabledSelection() { - Dialog dialog = mChooserDialog.getDialogForTesting(); - Assert.assertTrue(dialog.isShowing()); - - TestThreadUtils.runOnUiThreadBlocking(() -> { - mChooserDialog.addOrUpdateItem("key1", "desc1"); - mChooserDialog.addOrUpdateItem("key2", "desc2"); - }); - - // Disable one item and try to select it. - mChooserDialog.setEnabled("key1", false); - selectItem(dialog, 1, "None", false); - // The other is still selectable. - selectItem(dialog, 2, "key2", true); - - mChooserDialog.dismiss(); - } - - @Test - @LargeTest - public void testSelectOneItemThenDisableTheSelectedItem() throws Throwable { - final Dialog dialog = TestThreadUtils.runOnUiThreadBlocking(() -> { - Dialog dialog1 = mChooserDialog.getDialogForTesting(); - Assert.assertTrue(dialog1.isShowing()); - - mChooserDialog.addOrUpdateItem("key1", "desc1"); - mChooserDialog.addOrUpdateItem("key2", "desc2"); - return dialog1; - }); - - selectItem(dialog, 1, "key1", true); - TestThreadUtils.runOnUiThreadBlocking(() -> { - DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting(); - Assert.assertEquals("key1", itemAdapter.getSelectedItemKey()); - mChooserDialog.setEnabled("key1", false); - // The selected item is disabled, so no item is selected. - Assert.assertEquals("", itemAdapter.getSelectedItemKey()); - mChooserDialog.setEnabled("key1", true); - // The disabled item is not automatically selected again when it is re-enabled. - Assert.assertEquals("", itemAdapter.getSelectedItemKey()); - - mChooserDialog.dismiss(); - }); - } - - @Test - @LargeTest - public void testPairButtonDisabledOrEnabledAfterSelectedItemDisabledOrEnabled() - throws Throwable { - final Dialog dialog = TestThreadUtils.runOnUiThreadBlocking(() -> { - Dialog dialog1 = mChooserDialog.getDialogForTesting(); - Assert.assertTrue(dialog1.isShowing()); - - mChooserDialog.addOrUpdateItem("key1", "desc1"); - mChooserDialog.addOrUpdateItem("key2", "desc2"); - return dialog1; - }); - - selectItem(dialog, 1, "key1", true); - TestThreadUtils.runOnUiThreadBlocking(() -> { - final Button button = (Button) dialog.findViewById(R.id.positive); - Assert.assertTrue(button.isEnabled()); - - mChooserDialog.setEnabled("key1", false); - Assert.assertFalse(button.isEnabled()); - - mChooserDialog.setEnabled("key1", true); - // The disabled item is not automatically selected again when it is re-enabled, - // so the button is still disabled. - Assert.assertFalse(button.isEnabled()); - - mChooserDialog.dismiss(); - }); - } - - @Test - @LargeTest public void testPairButtonDisabledAfterSelectedItemRemoved() throws Throwable { final Dialog dialog = TestThreadUtils.runOnUiThreadBlocking(() -> { Dialog dialog1 = mChooserDialog.getDialogForTesting();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java index dcd5dfe1..a4078db 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
@@ -24,6 +24,7 @@ import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.ContextUtils; +import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.MinAndroidSdkLevel; @@ -31,6 +32,7 @@ import org.chromium.base.test.util.UrlUtils; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.ChromeTabbedActivity2; import org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.tabmodel.TabPersistentStoreTest.MockTabPersistentStoreObserver; @@ -44,12 +46,14 @@ import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.ui.test.util.UiRestriction; +import java.util.concurrent.TimeoutException; + /** * Tests merging tab models for Android N+ multi-instance. */ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) -@TargetApi(Build.VERSION_CODES.LOLLIPOP) +@TargetApi(Build.VERSION_CODES.N) @MinAndroidSdkLevel(Build.VERSION_CODES.N) public class TabModelMergingTest { @Rule @@ -71,6 +75,9 @@ private String[] mMergeIntoActivity1ExpectedTabs; private String[] mMergeIntoActivity2ExpectedTabs; + CallbackHelper mNewCTA2CallbackHelper = new CallbackHelper(); + private ChromeTabbedActivity2 mNewCTA2; + @Before public void setUp() throws Exception { mActivityTestRule.startMainActivityOnBlankPage(); @@ -113,6 +120,10 @@ mActivity1State = newState; } else if (activity.equals(mActivity2)) { mActivity2State = newState; + } else if (activity instanceof ChromeTabbedActivity2 + && newState == ActivityState.CREATED) { + mNewCTA2 = (ChromeTabbedActivity2) activity; + mNewCTA2CallbackHelper.notifyCalled(); } } }); @@ -255,7 +266,7 @@ @Test @LargeTest @Feature({"TabPersistentStore", "MultiWindow"}) - public void testMergeIntoChromeTabbedActivity1() throws Exception { + public void testMergeIntoChromeTabbedActivity1() { mergeTabsAndAssert(mActivity1, mMergeIntoActivity1ExpectedTabs); mActivity1.finishAndRemoveTask(); } @@ -263,7 +274,7 @@ @Test @LargeTest @Feature({"TabPersistentStore", "MultiWindow"}) - public void testMergeIntoChromeTabbedActivity2() throws Exception { + public void testMergeIntoChromeTabbedActivity2() { mergeTabsAndAssert(mActivity2, mMergeIntoActivity2ExpectedTabs); mActivity2.finishAndRemoveTask(); } @@ -271,7 +282,7 @@ @Test @LargeTest @Feature({"TabPersistentStore", "MultiWindow"}) - public void testMergeOnColdStart() throws Exception { + public void testMergeOnColdStart() { String expectedSelectedUrl = mActivity1.getTabModelSelector().getCurrentTab().getUrl(); // Create an intent to launch a new ChromeTabbedActivity. @@ -333,7 +344,7 @@ // Destroy ChromeTabbedActivity2. ChromeTabbedActivity should have been destroyed during the // merge. mActivity2.finishAndRemoveTask(); - CriteriaHelper.pollUiThread(new Criteria("Both activitie should be destroyed." + CriteriaHelper.pollUiThread(new Criteria("Both activities should be destroyed." + "CTA state: " + mActivity1State + " - CTA2State: " + mActivity2State) { @Override public boolean isSatisfied() { @@ -353,8 +364,63 @@ @Test @LargeTest @Feature({"TabPersistentStore", "MultiWindow"}) + public void testMergeOnColdStartIntoChromeTabbedActivity2() + throws TimeoutException, InterruptedException { + String CTA2ClassName = mActivity2.getClass().getName(); + String CTA2PackageName = mActivity2.getPackageName(); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + mActivity1.saveState(); + mActivity2.saveState(); + }); + + // Destroy both activities without removing tasks. + mActivity1.finish(); + mActivity2.finish(); + + CriteriaHelper.pollUiThread(new Criteria("Both activities should be destroyed." + + "CTA state: " + mActivity1State + " - CTA2State: " + mActivity2State) { + @Override + public boolean isSatisfied() { + return mActivity1State == ActivityState.DESTROYED + && mActivity2State == ActivityState.DESTROYED; + } + }); + + // Send a main intent to restart ChromeTabbedActivity2. + Intent CTA2MainIntent = new Intent(Intent.ACTION_MAIN); + CTA2MainIntent.setClassName(CTA2PackageName, CTA2ClassName); + InstrumentationRegistry.getInstrumentation().startActivitySync(CTA2MainIntent); + + mNewCTA2CallbackHelper.waitForCallback(0); + + CriteriaHelper.pollUiThread(new Criteria("CTA2 tab state failed to initialize.") { + @Override + public boolean isSatisfied() { + return mNewCTA2.areTabModelsInitialized() + && mNewCTA2.getTabModelSelector().isTabStateInitialized(); + } + }); + + // Check that a merge occurred. + Assert.assertEquals("Wrong number of tabs after restart.", + mMergeIntoActivity2ExpectedTabs.length, + mNewCTA2.getTabModelSelector().getModel(false).getCount()); + + // TODO(twellington): When manually testing with "Don't keep activities" turned on in + // developer settings, tabs are merged in the right order. In this test, however, the + // order isn't quite as expected. Investigate replacing #finish() with something that + // better simulates the activity being killed in the background due to OOM. + + // Clean up. + mNewCTA2.finishAndRemoveTask(); + } + + @Test + @LargeTest + @Feature({"TabPersistentStore", "MultiWindow"}) @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE}) - public void testMergeWhileInTabSwitcher() throws Exception { + public void testMergeWhileInTabSwitcher() { OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehaviorWatcher( mActivity1.getLayoutManager(), true, false); TestThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java index f09ea6a..d11b77eb 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/SiteSuggestionsLayoutManager.java
@@ -57,6 +57,10 @@ RecyclerView parent, RecyclerView.State state, View child, View focused) { if (focused != null) { int newChildPos = getPosition(focused); + + // Stop scrolling if we're in the middle of a swipe/fling, see https://crbug.com/982357. + parent.stopScroll(); + scrollToPosition(newChildPos); } return true;
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 3645e00..25df07e85 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -1701,91 +1701,103 @@ </message> </if> - <!-- Kerberos Accounts --> + <!-- Kerberos tickets --> <if expr="chromeos"> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SUBMENU_LABEL" desc="Label of Kerberos Accounts submenu in Settings page."> - Kerberos Accounts + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SUBMENU_LABEL" desc="Label of 'Kerberos tickets' submenu in Settings page."> + Kerberos tickets </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE" desc="Title of Kerberos Accounts Settings page."> - Kerberos Accounts + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE" desc="Title of 'Kerberos tickets' Settings page."> + Kerberos tickets </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_DESCRIPTION" desc="Description of the Kerberos Accounts Settings page. Shown just below the title of the page."> - All Kerberos Accounts for single sign-on into certain apps and websites and file shares can be managed here. + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_DESCRIPTION" desc="Description of the 'Kerberos tickets' Settings page. Shown just below the title of the page."> + Choose a ticket to use for authentication. <ph name="LINK_BEGIN"><a target="_blank" href="$1<ex>https://google.com/</ex>"></ph>Learn more<ph name="LINK_END"></a></ph> </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_LIST_HEADER" desc="List header for the account list on Kerberos Accounts Settings page."> - Accounts + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_LIST_HEADER" desc="List header for the ticket list on 'Kerberos tickets' Settings page."> + Tickets </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_ADD_ACCOUNT_LABEL" desc="Label of the Add account button on Kerberos Accounts Settings page."> - Add account + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_ADD_ACCOUNT_LABEL" desc="Label of the 'Add a ticket' button on 'Kerberos tickets' Settings page."> + Add a ticket </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REFRESH_NOW_LABEL" desc="Label of the 'Refresh now' button on Kerberos Accounts Settings page."> + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REFRESH_NOW_LABEL" desc="Label of the 'Refresh now' button on 'Kerberos tickets' Settings page."> Refresh now </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SET_AS_ACTIVE_ACCOUNT_LABEL" desc="Label of the 'Set as active account' button on Kerberos Accounts Settings page."> - Set as active account + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SET_AS_ACTIVE_ACCOUNT_LABEL" desc="Label of the 'Set as ticket ticket' button on 'Kerberos tickets' Settings page."> + Set as active ticket </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REMOVE_ACCOUNT_LABEL" desc="Label of the 'Remove account' button on Kerberos Accounts Settings page."> - Remove account from this device + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REMOVE_ACCOUNT_LABEL" desc="Label of the 'Remove from this device' button on 'Kerberos tickets' Settings page."> + Remove from this device </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_ADVANCED_CONFIG_LABEL" desc="Label of the button on Kerberos Accounts Settings page to get to the Advanced configuration dialog."> - Advanced configuration + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_ACCOUNT_REMOVED_TIP" desc="Tip shown when a Kerberos ticket was removed on 'Kerberos tickets' Settings page."> + Ticket removed </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_IN" desc="Text displayed if a Kerberos account is in signed-in state on Kerberos Accounts Settings page."> - Signed in + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_IN" desc="Text displayed if a Kerberos ticket is in signed-in state on 'Kerberos tickets' Settings page."> + Valid for <ph name="TICKET_TIME_LEFT">$1<ex>7 hours 12 minutes</ex></ph> </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_OUT" desc="Text displayed if a Kerberos account is in signed-out state on Kerberos Accounts Settings page."> - Sign in again + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_OUT" desc="Text displayed if a Kerberos ticket is in signed-out state on 'Kerberos tickets' Settings page."> + Expired </message> - <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL" desc="Label of the re-authentication button on Kerberos Accounts Settings page."> - Sign in + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL" desc="Label of the re-authentication button on 'Kerberos tickets' Settings page."> + Refresh </message> - <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT" desc="In Add Kerberos Accounts dialog, the title of the dialog."> - Add Kerberos Account + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_TICKET_ACTIVE" desc="Text displayed if a Kerberos ticket is selected to be the currently active ticket on 'Kerberos tickets' Settings page."> + Active </message> - <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REMEMBER_PASSWORD" desc="In Add Kerberos Accounts dialog, label of the checkbox to remember the password."> - Remember password + <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT" desc="Title of the 'Add a Kerberos ticket' dialog."> + Add a Kerberos ticket </message> - <message name="IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_TITLE" desc="In Kerberos Advanced Configuration dialog, the title of the dialog."> - Advanced Kerberos configuration + <message name="IDS_SETTINGS_REFRESH_KERBEROS_ACCOUNT" desc="Title of the 'Refresh a Kerberos ticket' dialog."> + Refresh a Kerberos ticket </message> - <message name="IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_DESC" desc="In Kerberos Advanced Configuration dialog, the description of the dialog (below the title)."> - Edit the Kerberos configuration file here. + <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_DESCRIPTION" desc="Description of the 'Add a Kerberos ticket' dialog. Shown just below the title of the dialog."> + To automatically refresh a ticket, check “Remember password.” Your password will be stored on your device only. </message> - <message name="IDS_SETTINGS_KERBEROS_USERNAME" desc="Title for the input that lets users specify their username for a Kerberos account."> - Username + <message name="IDS_SETTINGS_KERBEROS_USERNAME" desc="Title for the input that lets users specify their username for a Kerberos ticket."> + Kerberos username </message> - <message name="IDS_SETTINGS_KERBEROS_PASSWORD" desc="Title for the input that lets users specify their password for a Kerberos account."> + <message name="IDS_SETTINGS_KERBEROS_PASSWORD" desc="Title for the input that lets users specify their password for a Kerberos ticket."> Password </message> - <message name="IDS_SETTINGS_KERBEROS_CONFIG" desc="In Kerberos Advanced Configuration dialog, label of the Kerberos configuration text area."> - Configuration + <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REMEMBER_PASSWORD" desc="Label of the checkbox to remember the password in the 'Add a Kerberos ticket' dialog."> + Remember password </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_NETWORK_PROBLEM" desc="Error message displayed in the Add Kerberos Account dialog when the user has no network connection or enters a bad realm."> + <message name="IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REFRESH_BUTTON_LABEL" desc="Label of the action button on the 'Refresh a Kerberos tickets' dialog."> + Refresh + </message> + <message name="IDS_SETTINGS_KERBEROS_ACCOUNTS_ADVANCED_CONFIG_LABEL" desc="Label of the button in 'Add a Kerberos ticket' dialog to get to the Advanced configuration dialog."> + Advanced + </message> + <message name="IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_TITLE" desc="In Kerberos Advanced Configuration dialog, the title of the dialog."> + Configure Kerberos + </message> + <message name="IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_DESC" desc="In Kerberos Advanced Configuration dialog, the description of the dialog (below the title)."> + Edit the configuration file + </message> + <message name="IDS_SETTINGS_KERBEROS_ERROR_NETWORK_PROBLEM" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user has no network connection or enters a bad realm."> Network problem or bad realm </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_USERNAME_INVALID" desc="Error message displayed in the Add Kerberos Account dialog when the user enters a badly formatted username."> - Username invalid (should be user@realm.com) + <message name="IDS_SETTINGS_KERBEROS_ERROR_USERNAME_INVALID" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user enters a badly formatted username."> + Username invalid </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_USERNAME_UNKNOWN" desc="Error message displayed in the Add Kerberos Account dialog when the user enters a username that is not known to the Kerberos realm."> + <message name="IDS_SETTINGS_KERBEROS_ERROR_USERNAME_UNKNOWN" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user enters a username that is not known to the Kerberos realm."> Username not known to server </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_DUPLICATE_PRINCIPAL_NAME" desc="Error message displayed in the Add Kerberos Account dialog when the user enters a username for which there is already an account."> - An account with this username already exists + <message name="IDS_SETTINGS_KERBEROS_ERROR_DUPLICATE_PRINCIPAL_NAME" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user enters a username for which there is already a ticket."> + A ticket with this username already exists </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_CONTACTING_SERVER" desc="Error message displayed in the Add Kerberos Account dialog when the OS is not able to contact the server for the given realm."> + <message name="IDS_SETTINGS_KERBEROS_ERROR_CONTACTING_SERVER" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the OS is not able to contact the server for the given realm."> Contacting server for realm failed </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_INVALID" desc="Error message displayed in the Add Kerberos Account dialog when the user enters the wrong password."> + <message name="IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_INVALID" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user enters the wrong password."> Password invalid </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_EXPIRED" desc="Error message displayed in the Add Kerberos Account dialog when the user's password is expired."> + <message name="IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_EXPIRED" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the user's password is expired."> Password expired </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_KDC_ENC_TYPE" desc="Error message displayed in the Add Kerberos Account dialog when the KDC does not support the encryption types specified in the Kerberos configuration (KDC = Key Distribution Center, part of Kerberos infrastructure)."> + <message name="IDS_SETTINGS_KERBEROS_ERROR_KDC_ENC_TYPE" desc="Error message displayed in the 'Add a Kerberos ticket' dialog when the KDC does not support the encryption types specified in the Kerberos configuration (KDC = Key Distribution Center, part of Kerberos infrastructure)."> KDC does not support encryption type </message> - <message name="IDS_SETTINGS_KERBEROS_ERROR_GENERAL" desc="Fallback error message displayed in the Add Kerberos dialog."> - Oops! Something went wrong (error code <ph name="ERROR_CODE">$1<ex>123</ex></ph>). + <message name="IDS_SETTINGS_KERBEROS_ERROR_GENERAL" desc="Fallback error message displayed in the 'Add a Kerberos ticket' dialog."> + Couldn't get Kerberos ticket. Try again, or contact your organization's device admin. (Error code <ph name="ERROR_CODE">$1<ex>123</ex></ph>). </message> </if>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index f0f1b94f..16e05c2 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2138,6 +2138,11 @@ flag_descriptions::kPasswordImportDescription, kOsAll, FEATURE_VALUE_TYPE(password_manager::features::kPasswordImport)}, #if defined(OS_ANDROID) + {"password-editing-android", flag_descriptions::kPasswordEditingAndroidName, + flag_descriptions::kPasswordEditingAndroidDescription, kOsAndroid, + FEATURE_VALUE_TYPE(password_manager::features::kPasswordEditingAndroid)}, +#endif // OS_ANDROID +#if defined(OS_ANDROID) {"enable-android-web-contents-dark-mode", flag_descriptions::kAndroidWebContentsDarkMode, flag_descriptions::kAndroidWebContentsDarkModeDescription, kOsAndroid,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index ecd94cd5..6490f70 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -210,6 +210,7 @@ &omnibox::kQueryInOmnibox, &omnibox::kUIExperimentShowSuggestionFavicons, &password_manager::features::kGooglePasswordManager, + &password_manager::features::kPasswordEditingAndroid, &password_manager::features::kTouchToFillAndroid, &safe_browsing::kCaptureSafetyNetId, &signin::kMiceFeature,
diff --git a/chrome/browser/autofill/mock_autofill_popup_controller.h b/chrome/browser/autofill/mock_autofill_popup_controller.h index cde092f..164523ecc 100644 --- a/chrome/browser/autofill/mock_autofill_popup_controller.h +++ b/chrome/browser/autofill/mock_autofill_popup_controller.h
@@ -8,6 +8,7 @@ #include <memory> #include <vector> +#include "base/memory/weak_ptr.h" #include "base/no_destructor.h" #include "build/build_config.h" #include "chrome/browser/ui/autofill/autofill_popup_controller.h" @@ -18,7 +19,9 @@ namespace autofill { -class MockAutofillPopupController : public AutofillPopupController { +class MockAutofillPopupController + : public AutofillPopupController, + public base::SupportsWeakPtr<MockAutofillPopupController> { public: MockAutofillPopupController(); ~MockAutofillPopupController();
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc index 7053c9f..a0267e12 100644 --- a/chrome/browser/banners/app_banner_manager.cc +++ b/chrome/browser/banners/app_banner_manager.cc
@@ -242,7 +242,11 @@ AppBannerSettingsHelper::UpdateFromFieldTrial(); } -AppBannerManager::~AppBannerManager() { } +AppBannerManager::~AppBannerManager() { + for (Observer& observer : observer_list_) + observer.ObserveAppBannerManager(nullptr); + CHECK(!observer_list_.might_have_observers()); +} bool AppBannerManager::CheckIfShouldShowBanner() { if (ShouldBypassEngagementChecks())
diff --git a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc index 43be9402..2c4fde8c 100644 --- a/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc +++ b/chrome/browser/browsing_data/counters/browsing_data_counter_utils_unittest.cc
@@ -153,11 +153,15 @@ } kTestCases[] = { {0, 0, false, {}, "None"}, {0, 0, true, {}, "None"}, - {1, 0, false, {"a.com"}, "1 password for a.com"}, - {1, 0, true, {"a.com"}, "1 password for a.com (synced)"}, - {2, 0, false, {"a.com"}, "2 passwords for a.com"}, - {2, 0, false, {"a.com", "b.com"}, "2 passwords for a.com, b.com"}, - {2, 0, true, {"a.com", "b.com"}, "2 passwords for a.com, b.com (synced)"}, + {1, 0, false, {"a.com"}, "1 password (for a.com)"}, + {1, 0, true, {"a.com"}, "1 password (for a.com, synced)"}, + {2, 0, false, {"a.com"}, "2 passwords (for a.com)"}, + {2, 0, false, {"a.com", "b.com"}, "2 passwords (for a.com, b.com)"}, + {2, + 0, + true, + {"a.com", "b.com"}, + "2 passwords (for a.com, b.com, synced)"}, {0, 1, false, {}, "sign-in data for 1 account"}, {0, 1, true, {}, "sign-in data for 1 account"}, {0, 2, false, {}, "sign-in data for 2 accounts"}, @@ -166,29 +170,29 @@ 2, false, {"a.de"}, - "1 password for a.de; sign-in data for 2 accounts"}, + "1 password (for a.de); sign-in data for 2 accounts"}, {2, 1, false, {"a.de", "b.de"}, - "2 passwords for a.de, b.de; sign-in data for 1 account"}, + "2 passwords (for a.de, b.de); sign-in data for 1 account"}, {2, 3, true, {"a.de", "b.de"}, - "2 passwords for a.de, b.de (synced); sign-in data for 3 " + "2 passwords (for a.de, b.de, synced); sign-in data for 3 " "accounts"}, {4, 2, false, {"a.de", "b.de"}, - "4 passwords for a.de, b.de, and 2 more; sign-in data for 2 " + "4 passwords (for a.de, b.de, and 2 more); sign-in data for 2 " "accounts"}, {8, 0, true, {"a.de", "b.de", "c.de", "d.de", "e.de", "f.de", "g.de", "h.de"}, - "8 passwords for a.de, b.de, and 6 more (synced)"}}; + "8 passwords (for a.de, b.de, and 6 more, synced)"}}; for (const auto& test_case : kTestCases) { browsing_data::SigninDataCounter::SigninDataResult result( &counter, test_case.num_passwords, test_case.num_webauthn_credentials,
diff --git a/chrome/browser/chromeos/kerberos/kerberos_ticket_expiry_notification.cc b/chrome/browser/chromeos/kerberos/kerberos_ticket_expiry_notification.cc index c024e6a..97a4e96 100644 --- a/chrome/browser/chromeos/kerberos/kerberos_ticket_expiry_notification.cc +++ b/chrome/browser/chromeos/kerberos/kerberos_ticket_expiry_notification.cc
@@ -60,8 +60,6 @@ void Show(Profile* profile, const std::string& principal_name, ClickCallback click_callback) { - const base::string16 kDisplaySource = - l10n_util::GetStringUTF16(IDS_KERBEROS_TICKET_EXPIRY_DISPLAY_SOURCE); const base::string16 kTitle = l10n_util::GetStringUTF16(IDS_KERBEROS_TICKET_EXPIRY_TITLE); const base::string16 kBody = l10n_util::GetStringFUTF16( @@ -75,6 +73,9 @@ // No origin URL is needed since the notification comes from the system. const GURL kEmptyOriginUrl; + // Empty display source to show OS name as source. + const base::string16 kEmptyDisplaySource; + // Office building. const gfx::VectorIcon& kIcon = vector_icons::kBusinessIcon; @@ -87,7 +88,7 @@ base::BindRepeating(&OnClick, click_callback, principal_name); std::unique_ptr<Notification> notification = ash::CreateSystemNotification( - kNotificationType, kNotificationId, kTitle, kBody, kDisplaySource, + kNotificationType, kNotificationId, kTitle, kBody, kEmptyDisplaySource, kEmptyOriginUrl, kNotifierId, notification_data, base::MakeRefCounted<HandleNotificationClickDelegate>(callback_wrapper), kIcon, kWarningLevel);
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc index 093b871..2ef8cd8 100644 --- a/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc +++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen_browsertest.cc
@@ -557,7 +557,8 @@ EXPECT_EQ(base::Value(base::Value::Type::LIST), *fast_reinstall_packages); } -IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, NoRecommendedApps) { +// Disabled due to flakiness: https://crbug.com/982161 +IN_PROC_BROWSER_TEST_F(RecommendAppsScreenTest, DISABLED_NoRecommendedApps) { recommend_apps_screen_->Show(); OobeScreenWaiter screen_waiter(RecommendAppsScreenView::kScreenId);
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc index 544c44c..6593fbd 100644 --- a/chrome/browser/chromeos/policy/system_log_uploader.cc +++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -11,6 +11,8 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/files/scoped_temp_dir.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" @@ -22,12 +24,14 @@ #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" #include "chrome/browser/policy/policy_conversions.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "components/feedback/anonymizer_tool.h" #include "components/policy/core/browser/browser_policy_connector.h" #include "components/user_manager/user_manager.h" #include "net/http/http_request_headers.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "third_party/zlib/google/zip.h" namespace policy { @@ -46,6 +50,9 @@ // there is no actual file on disk. constexpr char kPolicyDumpFileLocation[] = "/var/log/policy_dump.json"; +// Name used for file containing zip archive of the logs. +constexpr char kZippedLogsFile[] = "logs.zip"; + // The file names of the system logs to upload. // Note: do not add anything to this list without checking for PII in the file. const char* const kSystemLogFileNames[] = { @@ -56,6 +63,53 @@ "/var/log/net.log", "/var/log/net.1.log", "/var/log/ui/ui.LATEST", "/var/log/update_engine.log"}; +std::string ZipFiles( + std::unique_ptr<SystemLogUploader::SystemLogs> system_logs) { + base::ScopedTempDir temp_dir; + base::FilePath zip_file; + std::string compressed_logs; + auto zipped_logs = std::make_unique<SystemLogUploader::SystemLogs>(); + + if (!temp_dir.CreateUniqueTempDir()) + return compressed_logs; + + std::vector<base::FilePath> file_names; + for (const auto& syslog_entry : *system_logs) { + base::FilePath file_path(temp_dir.GetPath().Append(syslog_entry.first)); + base::FilePath relative_path; + temp_dir.GetPath().AppendRelativePath(file_path, &relative_path); + + if (!base::CreateDirectory(file_path.DirName())) { + PLOG(ERROR) << "Can't create directory for log file: " + << file_path.value(); + continue; + } + if (!base::WriteFile(file_path, syslog_entry.second.c_str(), + syslog_entry.second.size())) { + PLOG(ERROR) << "Can't write log file: " << file_path.value(); + continue; + } + file_names.push_back(relative_path); + } + system_logs.reset(); + + base::ScopedFILE file(base::CreateAndOpenTemporaryFile(&zip_file)); + if (!file.get()) { + PLOG(ERROR) << "Failed to create file to store zipped logs"; + return compressed_logs; + } + if (!zip::ZipFiles(temp_dir.GetPath(), file_names, fileno(file.get()))) { + SYSLOG(ERROR) << "Failed to zip system logs"; + return compressed_logs; + }; + if (!base::ReadFileToString(zip_file, &compressed_logs)) { + PLOG(ERROR) << "Failed to read zipped system logs"; + return compressed_logs; + } + base::DeleteFile(zip_file, false); + return compressed_logs; +} + std::string ReadAndAnonymizeLogFile(feedback::AnonymizerTool* anonymizer, const base::FilePath& file_path) { std::string data; @@ -105,6 +159,9 @@ const GURL& upload_url, UploadJob::Delegate* delegate) override; + void ZipSystemLogs(std::unique_ptr<SystemLogUploader::SystemLogs> system_logs, + ZippedLogUploadCallback upload_callback) override; + private: // TaskRunner used for scheduling upload the upload task. const scoped_refptr<base::SequencedTaskRunner> task_runner_; @@ -178,6 +235,15 @@ traffic_annotation, task_runner_); } +void SystemLogDelegate::ZipSystemLogs( + std::unique_ptr<SystemLogUploader::SystemLogs> system_logs, + ZippedLogUploadCallback upload_callback) { + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce(&ZipFiles, std::move(system_logs)), + std::move(upload_callback)); +} + // Returns the system log upload frequency. base::TimeDelta GetUploadFrequency() { base::TimeDelta upload_frequency(base::TimeDelta::FromMilliseconds( @@ -223,6 +289,16 @@ // Template string constant for populating the name field. const char* const SystemLogUploader::kNameFieldTemplate = "file%d"; +// String constant signalling that the data segment contains zipped log files. +const char* const SystemLogUploader::kFileTypeZippedLogFile = "zipped_log_file"; + +// String constant for zipped logs name. +const char* const SystemLogUploader::kZippedLogsName = "logs"; + +// String constant signalling that the segment contains a binary file. +const char* const SystemLogUploader::kContentTypeOctetStream = + "application/octet-stream"; + SystemLogUploader::SystemLogUploader( std::unique_ptr<Delegate> syslog_delegate, const scoped_refptr<base::SequencedTaskRunner>& task_runner) @@ -346,6 +422,35 @@ upload_job_->Start(); } +void SystemLogUploader::UploadZippedSystemLogs(std::string zipped_system_logs) { + // Must be called on the main thread. + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!upload_job_); + + if (zipped_system_logs.empty()) { + SYSLOG(ERROR) << "No zipped log to upload"; + return; + } + + SYSLOG(INFO) << "Uploading zipped system logs."; + + GURL upload_url(GetUploadUrl()); + DCHECK(upload_url.is_valid()); + upload_job_ = syslog_delegate_->CreateUploadJob(upload_url, this); + + // Start a system log upload. + std::map<std::string, std::string> header_fields; + std::unique_ptr<std::string> data = + std::make_unique<std::string>(zipped_system_logs); + header_fields.insert( + std::make_pair(kFileTypeHeaderName, kFileTypeZippedLogFile)); + header_fields.insert(std::make_pair(net::HttpRequestHeaders::kContentType, + kContentTypeOctetStream)); + upload_job_->AddDataSegment(kZippedLogsName, kZippedLogsFile, header_fields, + std::move(data)); + upload_job_->Start(); +} + void SystemLogUploader::StartLogUpload() { // Must be called on the main thread. DCHECK(thread_checker_.CalledOnValidThread()); @@ -370,8 +475,17 @@ DCHECK(thread_checker_.CalledOnValidThread()); system_logs->push_back(std::make_pair(kPolicyDumpFileLocation, syslog_delegate_->GetPolicyAsJSON())); - SYSLOG(INFO) << "Starting system log upload."; - UploadSystemLogs(std::move(system_logs)); + + if (base::FeatureList::IsEnabled(features::kUploadZippedSystemLogs)) { + SYSLOG(INFO) << "Starting zipped system log upload."; + syslog_delegate_->ZipSystemLogs( + std::move(system_logs), + base::BindOnce(&SystemLogUploader::UploadZippedSystemLogs, + weak_factory_.GetWeakPtr())); + } else { + SYSLOG(INFO) << "Starting system log upload."; + UploadSystemLogs(std::move(system_logs)); + } } void SystemLogUploader::ScheduleNextSystemLogUpload(base::TimeDelta frequency) {
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.h b/chrome/browser/chromeos/policy/system_log_uploader.h index be4f51c..3726885 100644 --- a/chrome/browser/chromeos/policy/system_log_uploader.h +++ b/chrome/browser/chromeos/policy/system_log_uploader.h
@@ -48,12 +48,17 @@ static const int64_t kDefaultUploadDelayMs; static const int64_t kErrorUploadDelayMs; - // Http header constants to upload. + // Http header constants to upload non-zipped logs. static const char* const kNameFieldTemplate; static const char* const kFileTypeHeaderName; static const char* const kFileTypeLogFile; static const char* const kContentTypePlainText; + // Http header constants to upload zipped logs. + static const char* const kFileTypeZippedLogFile; + static const char* const kZippedLogsName; + static const char* const kContentTypeOctetStream; + // A delegate interface used by SystemLogUploader to read the system logs // from the disk and create an upload job. class Delegate { @@ -61,6 +66,9 @@ using LogUploadCallback = base::OnceCallback<void(std::unique_ptr<SystemLogs> system_logs)>; + using ZippedLogUploadCallback = + base::OnceCallback<void(std::string zipped_system_logs)>; + virtual ~Delegate() {} // Returns current policy dump in JSON format. @@ -74,6 +82,10 @@ virtual std::unique_ptr<UploadJob> CreateUploadJob( const GURL& upload_url, UploadJob::Delegate* delegate) = 0; + + // Zips system logs in a single zip archive and invokes |upload_callback|. + virtual void ZipSystemLogs(std::unique_ptr<SystemLogs> system_logs, + ZippedLogUploadCallback upload_callback) = 0; }; // Constructor. Callers can inject their own Delegate. A nullptr can be passed @@ -112,6 +124,9 @@ // Uploads system logs. void UploadSystemLogs(std::unique_ptr<SystemLogs> system_logs); + // Uploads zipped system logs. + void UploadZippedSystemLogs(std::string zipped_system_logs); + // Helper method that figures out when the next system log upload should // be scheduled. void ScheduleNextSystemLogUpload(base::TimeDelta frequency);
diff --git a/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc b/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc index 3c23e44..c92da6e 100644 --- a/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc +++ b/chrome/browser/chromeos/policy/system_log_uploader_unittest.cc
@@ -143,6 +143,9 @@ system_logs_.size() + 1); } + void ZipSystemLogs(std::unique_ptr<SystemLogUploader::SystemLogs> system_logs, + ZippedLogUploadCallback upload_callback) override {} + void set_upload_allowed(bool is_upload_allowed) { is_upload_allowed_ = is_upload_allowed; }
diff --git a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc index 4b77e1d..7b80e96 100644 --- a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc +++ b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
@@ -44,6 +44,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/common/isolated_world_ids.h" #include "content/public/test/browser_test_utils.h" +#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" @@ -102,6 +103,16 @@ return entry; } +void ExpectBodyHasThemeAndFont(content::WebContents* contents, + const std::string& expected_theme, + const std::string& expected_font) { + std::string result; + EXPECT_TRUE( + content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); + EXPECT_THAT(result, HasSubstr(expected_theme)); + EXPECT_THAT(result, HasSubstr(expected_font)); +} + } // namespace class DomDistillerViewerSourceBrowserTest : public InProcessBrowserTest { @@ -557,10 +568,7 @@ browser()->tab_strip_model()->GetActiveWebContents(); ViewSingleDistilledPage(url, "text/html"); content::WaitForLoadStop(contents); - std::string result; - EXPECT_TRUE( - content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); - EXPECT_EQ("light sans-serif", result); + ExpectBodyHasThemeAndFont(contents, "light", "sans-serif"); DistilledPagePrefs* distilled_page_prefs = DomDistillerServiceFactory::GetForBrowserContext(browser()->profile()) @@ -569,21 +577,19 @@ // Test theme. distilled_page_prefs->SetTheme(DistilledPagePrefs::THEME_DARK); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE( - content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); - EXPECT_EQ("dark sans-serif", result); + ExpectBodyHasThemeAndFont(contents, "dark", "sans-serif"); // Verify that the theme color for the tab is updated as well. EXPECT_EQ(kDarkToolbarThemeColor, contents->GetThemeColor()); // Test font family. - distilled_page_prefs->SetFontFamily(DistilledPagePrefs::FONT_FAMILY_SERIF); + distilled_page_prefs->SetFontFamily( + DistilledPagePrefs::FONT_FAMILY_MONOSPACE); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE( - content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); - EXPECT_EQ("dark serif", result); + ExpectBodyHasThemeAndFont(contents, "dark", "monospace"); // Test font scaling. + std::string result; EXPECT_TRUE( content::ExecuteScriptAndExtractString(contents, kGetFontSize, &result)); double oldFontSize; @@ -621,13 +627,13 @@ // Set preference. const double kScale = 1.23; distilled_page_prefs->SetTheme(DistilledPagePrefs::THEME_DARK); - distilled_page_prefs->SetFontFamily(DistilledPagePrefs::FONT_FAMILY_SERIF); + distilled_page_prefs->SetFontFamily( + DistilledPagePrefs::FONT_FAMILY_MONOSPACE); distilled_page_prefs->SetFontScaling(kScale); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE( - content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); - EXPECT_EQ("dark serif", result); + ExpectBodyHasThemeAndFont(contents, "dark", "monospace"); + EXPECT_EQ(kDarkToolbarThemeColor, contents->GetThemeColor()); EXPECT_TRUE( content::ExecuteScriptAndExtractString(contents, kGetFontSize, &result)); @@ -643,7 +649,7 @@ base::RunLoop().RunUntilIdle(); EXPECT_TRUE( content::ExecuteScriptAndExtractString(contents, kGetBodyClass, &result)); - EXPECT_EQ("dark serif", result); + ExpectBodyHasThemeAndFont(contents, "dark", "monospace"); EXPECT_EQ(kDarkToolbarThemeColor, contents->GetThemeColor()); EXPECT_TRUE(
diff --git a/chrome/browser/download/download_request_limiter.cc b/chrome/browser/download/download_request_limiter.cc index 9b375961..c34eddc 100644 --- a/chrome/browser/download/download_request_limiter.cc +++ b/chrome/browser/download/download_request_limiter.cc
@@ -310,17 +310,30 @@ } void DownloadRequestLimiter::TabDownloadState::OnUserInteraction() { - bool promptable = - PermissionRequestManager::FromWebContents(web_contents()) != nullptr; - // See PromptUserForDownload(): if there's no PermissionRequestManager, then // DOWNLOADS_NOT_ALLOWED is functionally equivalent to PROMPT_BEFORE_DOWNLOAD. - if ((status_ != DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS) && - (!promptable || - (status_ != DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED))) { - // Revert to default status. - host_->Remove(this, web_contents()); - // WARNING: We've been deleted. + bool need_prompt = + (PermissionRequestManager::FromWebContents(web_contents()) == nullptr && + status_ == DOWNLOADS_NOT_ALLOWED) || + status_ == PROMPT_BEFORE_DOWNLOAD; + + // If content setting blocks automatic downloads, don't reset the + // PROMPT_BEFORE_DOWNLOAD status for the current page because doing + // that will default the download status to ALLOW_ONE_DOWNLOAD. That + // will allow an extra download when CanDownloadImpl() is called. + ContentSetting setting = GetAutoDownloadContentSetting( + web_contents(), web_contents()->GetVisibleURL()); + if (status_ == ALLOW_ONE_DOWNLOAD || + (need_prompt && setting != CONTENT_SETTING_BLOCK)) { + GURL origin = web_contents()->GetVisibleURL().GetOrigin(); + + // Revert to default status and notify if needed. + if (!origin.is_empty()) + download_status_map_.erase(origin); + if (download_status_map_.empty()) { + host_->Remove(this, web_contents()); + // WARNING: We've been deleted. + } } } @@ -553,6 +566,19 @@ Profile::FromBrowserContext(contents->GetBrowserContext())); } +ContentSetting DownloadRequestLimiter::GetAutoDownloadContentSetting( + content::WebContents* contents, + const GURL& request_initiator) { + HostContentSettingsMap* content_settings = GetContentSettings(contents); + ContentSetting setting = CONTENT_SETTING_ASK; + if (content_settings) { + setting = content_settings->GetContentSetting( + request_initiator, request_initiator, + CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, std::string()); + } + return setting; +} + void DownloadRequestLimiter::CanDownloadImpl( content::WebContents* originating_contents, const std::string& request_method, @@ -574,22 +600,18 @@ // Always check for the content setting first. Having an content setting // observer won't work as |request_initiator| might be different from the tab // URL. - HostContentSettingsMap* content_settings = - GetContentSettings(originating_contents); - ContentSetting setting = CONTENT_SETTING_ASK; - if (content_settings) { - setting = content_settings->GetContentSetting( - initiator, initiator, CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, - std::string()); - // Override the status if content setting is block. If the content setting - // is always allow, only reset the status if it is |DOWNLOADS_NOT_ALLOWED| - // so unnecessary notifications will not be triggered. - if (setting == CONTENT_SETTING_BLOCK) { - status = DOWNLOADS_NOT_ALLOWED; - } else if (setting == CONTENT_SETTING_ALLOW && - status == DOWNLOADS_NOT_ALLOWED) { - status = ALLOW_ALL_DOWNLOADS; - } + ContentSetting setting = + GetAutoDownloadContentSetting(originating_contents, initiator); + // Override the status if content setting is block or allow. If the content + // setting is always allow, only reset the status if it is + // DOWNLOADS_NOT_ALLOWED so unnecessary notifications will not be triggered. + // If the content setting is block, allow only one download to proceed if the + // current status is ALLOW_ALL_DOWNLOADS. + if (setting == CONTENT_SETTING_BLOCK && status == ALLOW_ALL_DOWNLOADS) { + status = ALLOW_ONE_DOWNLOAD; + } else if (setting == CONTENT_SETTING_ALLOW && + status == DOWNLOADS_NOT_ALLOWED) { + status = ALLOW_ALL_DOWNLOADS; } // Always call SetDownloadStatusAndNotify since we may need to change the
diff --git a/chrome/browser/download/download_request_limiter.h b/chrome/browser/download/download_request_limiter.h index 83d3542..35f8f2f 100644 --- a/chrome/browser/download/download_request_limiter.h +++ b/chrome/browser/download/download_request_limiter.h
@@ -298,6 +298,11 @@ static HostContentSettingsMap* GetContentSettings( content::WebContents* contents); + // Gets the content setting for a particular request initiator. + static ContentSetting GetAutoDownloadContentSetting( + content::WebContents* contents, + const GURL& request_initiator); + // Sets the callback for tests to know the result of OnCanDownloadDecided(). using CanDownloadDecidedCallback = base::RepeatingCallback<void(bool /*allow*/)>;
diff --git a/chrome/browser/download/download_request_limiter_unittest.cc b/chrome/browser/download/download_request_limiter_unittest.cc index 57b1285..4188b6b 100644 --- a/chrome/browser/download/download_request_limiter_unittest.cc +++ b/chrome/browser/download/download_request_limiter_unittest.cc
@@ -911,10 +911,10 @@ // Trigger a renderer initiated download from the other origin. CanDownloadFor(web_contents(), url::Origin::Create(GURL("http://foobar.com"))); - ExpectAndResetCounts(0, 1, 0, __LINE__); - EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, + ExpectAndResetCounts(1, 0, 0, __LINE__); + EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, download_request_limiter_->GetDownloadStatus(web_contents())); - EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED, + EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT, download_request_limiter_->GetDownloadUiStatus(web_contents())); // The current tab is not affected, still allowing one download. @@ -945,7 +945,7 @@ EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED, download_request_limiter_->GetDownloadUiStatus(web_contents())); - // Download should proceed work for the other origin. + // Download should proceed for the other origin. CanDownloadFor(web_contents(), url::Origin::Create(GURL("http://foobar.com"))); ExpectAndResetCounts(1, 0, 0, __LINE__); @@ -954,3 +954,47 @@ EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_ALLOWED, download_request_limiter_->GetDownloadUiStatus(web_contents())); } + +// Test that user interaction on the current page won't reset download status +// for another origin. +TEST_F(DownloadRequestLimiterTest, + DownloadStatusForOtherOriginsNotResetOnUserInteraction) { + NavigateAndCommit(GURL("http://foo.com/bar")); + LoadCompleted(); + + // Trigger a renderer initiated download from the other origin. + CanDownloadFor(web_contents(), + url::Origin::Create(GURL("http://foobar.com"))); + ExpectAndResetCounts(1, 0, 0, __LINE__); + EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, + download_request_limiter_->GetDownloadStatus(web_contents())); + EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT, + download_request_limiter_->GetDownloadUiStatus(web_contents())); + + // The current tab is not affected, still allowing one download. + CanDownloadFor(web_contents()); + ExpectAndResetCounts(1, 0, 0, __LINE__); + EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, + download_request_limiter_->GetDownloadStatus(web_contents())); + EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT, + download_request_limiter_->GetDownloadUiStatus(web_contents())); + + // On user interaction, the current page should reset its download status. + OnUserInteraction(blink::WebInputEvent::kTouchStart); + CanDownloadFor(web_contents()); + ExpectAndResetCounts(1, 0, 0, __LINE__); + EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD, + download_request_limiter_->GetDownloadStatus(web_contents())); + EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_DEFAULT, + download_request_limiter_->GetDownloadUiStatus(web_contents())); + + // Download status from the other origin does not reset and should prompt. + UpdateExpectations(CANCEL); + CanDownloadFor(web_contents(), + url::Origin::Create(GURL("http://foobar.com"))); + ExpectAndResetCounts(0, 1, 1, __LINE__); + EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED, + download_request_limiter_->GetDownloadStatus(web_contents())); + EXPECT_EQ(DownloadRequestLimiter::DOWNLOAD_UI_BLOCKED, + download_request_limiter_->GetDownloadUiStatus(web_contents())); +}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 1bd4259..ec7f6305 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2667,6 +2667,11 @@ "expiry_milestone": 78 }, { + "name": "password-editing-android", + "owners": [ "izuzic", "fhorschig" ], + "expiry_milestone": 80 + }, + { "name": "passwords-keyboard-accessory", "owners": [ "fhorschig" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 0f82b5f6..5e3762c 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1498,6 +1498,10 @@ "Forces wheel, and mousewheel event listeners on document level targets " "(which haven't requested otherwise) to be treated as passive."; +const char kPasswordEditingAndroidName[] = "Password editing for Android"; +const char kPasswordEditingAndroidDescription[] = + "Adds the editing option for saved passwords."; + const char kPasswordImportName[] = "Password import"; const char kPasswordImportDescription[] = "Import functionality in password settings.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index d983668..31798884 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -879,6 +879,9 @@ extern const char kParallelDownloadingName[]; extern const char kParallelDownloadingDescription[]; +extern const char kPasswordEditingAndroidName[]; +extern const char kPasswordEditingAndroidDescription[]; + extern const char kPassiveEventListenerDefaultName[]; extern const char kPassiveEventListenerDefaultDescription[]; extern const char kPassiveEventListenerTrue[];
diff --git a/chrome/browser/net/variations_http_headers_browsertest.cc b/chrome/browser/net/variations_http_headers_browsertest.cc index 8b19c7a6..3d97f9e 100644 --- a/chrome/browser/net/variations_http_headers_browsertest.cc +++ b/chrome/browser/net/variations_http_headers_browsertest.cc
@@ -325,9 +325,17 @@ EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data")); } +#if defined(OS_CHROMEOS) +// See https://crbug.com/964338 +#define MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext \ + DISABLED_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext +#else +#define MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext \ + TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext +#endif IN_PROC_BROWSER_TEST_F( VariationsHttpHeadersBrowserTest, - TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext) { + MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext) { GURL url = GetGoogleRedirectUrl1(); auto resource_request = std::make_unique<network::ResourceRequest>(); @@ -348,7 +356,6 @@ // Wait for the response to complete. loader_helper.WaitForCallback(); - EXPECT_EQ(net::OK, loader->NetError()); EXPECT_TRUE(loader_helper.response_body()); EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data")); @@ -357,9 +364,17 @@ EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data")); } +#if defined(OS_CHROMEOS) +// See https://crbug.com/964338 +#define MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext \ + DISABLED_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext +#else +#define MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext \ + TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext +#endif IN_PROC_BROWSER_TEST_F( VariationsHttpHeadersBrowserTest, - TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext) { + MAYBE_TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext) { GURL url = GetGoogleRedirectUrl1(); auto resource_request = std::make_unique<network::ResourceRequest>(); @@ -380,7 +395,6 @@ // Wait for the response to complete. loader_helper.WaitForCallback(); - EXPECT_EQ(net::OK, loader->NetError()); EXPECT_TRUE(loader_helper.response_body()); EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.cc b/chrome/browser/password_manager/touch_to_fill_controller.cc index 6b82f05..7e754a7 100644 --- a/chrome/browser/password_manager/touch_to_fill_controller.cc +++ b/chrome/browser/password_manager/touch_to_fill_controller.cc
@@ -4,13 +4,17 @@ #include "chrome/browser/password_manager/touch_to_fill_controller.h" +#include <string> #include <utility> #include "base/feature_list.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/autofill/manual_filling_controller.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller.h" +#include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/password_manager/core/common/password_manager_features.h" @@ -42,12 +46,22 @@ } void TouchToFillController::Show( - const std::vector<autofill::Suggestion>& suggestions) { + base::span<const autofill::Suggestion> suggestions, + base::WeakPtr<autofill::AutofillPopupController> popup_controller) { + popup_controller_ = std::move(popup_controller); + autofill::AccessorySheetData::Builder builder( autofill::AccessoryTabType::TOUCH_TO_FILL, // TODO(crbug.com/957532): Update title once mocks are finalized. base::ASCIIToUTF16("Touch to Fill")); - for (const auto& suggestion : suggestions) { + for (size_t i = 0; i < suggestions.size(); ++i) { + const auto& suggestion = suggestions[i]; + // Ignore suggestions that don't directly correspond to user credentials. + if (suggestion.frontend_id != autofill::POPUP_ITEM_ID_USERNAME_ENTRY && + suggestion.frontend_id != autofill::POPUP_ITEM_ID_PASSWORD_ENTRY) { + continue; + } + // This needs to stay in sync with how the PasswordAutofillManager creates // Suggestions out of PasswordFormFillData. const base::string16& username = suggestion.value; @@ -56,10 +70,15 @@ // form. const base::string16& maybe_realm = suggestion.label; - builder.AddUserInfo().AppendSimpleField(username); - builder.AppendField(password, password, /*is_obfuscated=*/true, + std::string field_id = base::NumberToString(i); + builder.AddUserInfo(); + builder.AppendField(username, username, field_id, + /*is_obfuscated=*/false, + /*is_selectable=*/true); + builder.AppendField(password, password, field_id, /*is_obfuscated=*/true, /*is_selectable=*/false); - builder.AppendField(maybe_realm, maybe_realm, /*is_obfuscated=*/false, + builder.AppendField(maybe_realm, maybe_realm, std::move(field_id), + /*is_obfuscated=*/false, /*is_selectable=*/false); } @@ -68,14 +87,32 @@ void TouchToFillController::OnFillingTriggered( const autofill::UserInfo::Field& selection) { - // TODO(crbug.com/957532): Implement this method. - NOTIMPLEMENTED(); + if (!popup_controller_) { + LOG(DFATAL) << "|popup_controller_| is not set or has been invalidated."; + return; + } + + int index = 0; + if (!base::StringToInt(selection.id(), &index)) { + LOG(DFATAL) << "Failed to convert selection.id(): " << selection.id(); + return; + } + + if (popup_controller_->GetLineCount() <= index) { + LOG(DFATAL) << "Received invalid suggestion index: " << index; + return; + } + + // Ivalidate |popup_controller_| to ignore future invocations of + // OnFillingTriggered for the same suggestions. + std::exchange(popup_controller_, nullptr)->AcceptSuggestion(index); } void TouchToFillController::OnOptionSelected( autofill::AccessoryAction selected_action) { - // TODO(crbug.com/957532): Implement this method. - NOTIMPLEMENTED(); + // Not applicable for TouchToFillController. All user interactions should + // result in OnFillingTriggered(). + NOTREACHED(); } TouchToFillController::TouchToFillController(WebContents* web_contents)
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.h b/chrome/browser/password_manager/touch_to_fill_controller.h index 66c4c6f6..61ea683 100644 --- a/chrome/browser/password_manager/touch_to_fill_controller.h +++ b/chrome/browser/password_manager/touch_to_fill_controller.h
@@ -8,11 +8,13 @@ #include <memory> #include <vector> +#include "base/containers/span.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/autofill/accessory_controller.h" #include "content/public/browser/web_contents_user_data.h" namespace autofill { +class AutofillPopupController; struct Suggestion; } @@ -46,7 +48,10 @@ static bool AllowedForWebContents(content::WebContents* web_contents); // Instructs the controller to show the provided |suggestions| to the user. - void Show(const std::vector<autofill::Suggestion>& suggestions); + // Invokes AcceptSuggestion() on popup_controller once the user made a + // selection. + void Show(base::span<const autofill::Suggestion> suggestions, + base::WeakPtr<autofill::AutofillPopupController> popup_controller); // AccessoryController: void OnFillingTriggered(const autofill::UserInfo::Field& selection) override; @@ -70,6 +75,9 @@ // The tab for which this class is scoped. content::WebContents* web_contents_ = nullptr; + // Popup controller passed from the latest invocation of Show(). + base::WeakPtr<autofill::AutofillPopupController> popup_controller_; + // The manual filling controller object to forward client requests to. base::WeakPtr<ManualFillingController> mf_controller_;
diff --git a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc index 0763b53..446d0550 100644 --- a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc +++ b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
@@ -6,18 +6,26 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/autofill/mock_autofill_popup_controller.h" #include "chrome/browser/autofill/mock_manual_filling_controller.h" +#include "components/autofill/core/browser/ui/popup_item_ids.h" #include "components/autofill/core/browser/ui/suggestion.h" #include "components/password_manager/core/common/password_manager_features.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using autofill::MockAutofillPopupController; + class TouchToFillControllerTest : public testing::Test { protected: MockManualFillingController& manual_filling_controller() { return mock_manual_filling_controller_; } + MockAutofillPopupController& popup_controller() { + return mock_popup_controller_; + } + TouchToFillController* touch_to_fill_controller() { return touch_to_fill_controller_.get(); } @@ -25,6 +33,7 @@ private: testing::StrictMock<MockManualFillingController> mock_manual_filling_controller_; + MockAutofillPopupController mock_popup_controller_; std::unique_ptr<TouchToFillController> touch_to_fill_controller_ = TouchToFillController::CreateForTesting( mock_manual_filling_controller_.AsWeakPtr()); @@ -58,10 +67,17 @@ autofill::Suggestion alice(alice_user); alice.additional_label = alice_pass; + alice.frontend_id = autofill::POPUP_ITEM_ID_USERNAME_ENTRY; autofill::Suggestion bob(bob_user); bob.additional_label = bob_pass; bob.label = bob_realm; + bob.frontend_id = autofill::POPUP_ITEM_ID_PASSWORD_ENTRY; + + // Add an "All Saved Passwords" entry, which should be ignored. + autofill::Suggestion all_passwords; + all_passwords.frontend_id = autofill::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY; + popup_controller().set_suggestions({alice, bob, all_passwords}); EXPECT_CALL(manual_filling_controller(), RefreshSuggestions( @@ -69,14 +85,18 @@ autofill::AccessoryTabType::TOUCH_TO_FILL, base::ASCIIToUTF16("Touch to Fill")) .AddUserInfo() - .AppendField(alice_user, alice_user, false, true) - .AppendField(alice_pass, alice_pass, true, false) - .AppendField(alice_realm, alice_realm, false, false) + .AppendField(alice_user, alice_user, "0", false, true) + .AppendField(alice_pass, alice_pass, "0", true, false) + .AppendField(alice_realm, alice_realm, "0", false, false) .AddUserInfo() - .AppendField(bob_user, bob_user, false, true) - .AppendField(bob_pass, bob_pass, true, false) - .AppendField(bob_realm, bob_realm, false, false) + .AppendField(bob_user, bob_user, "1", false, true) + .AppendField(bob_pass, bob_pass, "1", true, false) + .AppendField(bob_realm, bob_realm, "1", false, false) .Build())); + touch_to_fill_controller()->Show(popup_controller().GetSuggestions(), + popup_controller().AsWeakPtr()); - touch_to_fill_controller()->Show({alice, bob}); + EXPECT_CALL(popup_controller(), AcceptSuggestion(1)); + touch_to_fill_controller()->OnFillingTriggered( + autofill::UserInfo::Field(bob_user, bob_user, "1", false, true)); }
diff --git a/chrome/browser/payments/ssl_validity_checker.cc b/chrome/browser/payments/ssl_validity_checker.cc index b1c7957..c71e8f9 100644 --- a/chrome/browser/payments/ssl_validity_checker.cc +++ b/chrome/browser/payments/ssl_validity_checker.cc
@@ -8,7 +8,7 @@ #include "base/logging.h" #include "chrome/browser/ssl/security_state_tab_helper.h" #include "components/network_session_configurator/common/network_switches.h" -#include "components/payments/core/error_strings.h" +#include "components/payments/core/native_error_strings.h" #include "components/payments/core/url_util.h" #include "components/security_state/core/security_state.h" #include "url/gurl.h"
diff --git a/chrome/browser/prerender/OWNERS b/chrome/browser/prerender/OWNERS index 89397a9..4fcb149 100644 --- a/chrome/browser/prerender/OWNERS +++ b/chrome/browser/prerender/OWNERS
@@ -4,7 +4,6 @@ # Deprecated reviewers. lizeb@chromium.org -mattcary@chromium.org pasko@chromium.org # COMPONENT: Internals>Preload
diff --git a/chrome/browser/previews/previews_top_host_provider_impl.cc b/chrome/browser/previews/previews_top_host_provider_impl.cc index ebb57fb..3c638b7 100644 --- a/chrome/browser/previews/previews_top_host_provider_impl.cc +++ b/chrome/browser/previews/previews_top_host_provider_impl.cc
@@ -10,10 +10,10 @@ #include "chrome/browser/engagement/site_engagement_score.h" #include "chrome/browser/engagement/site_engagement_service.h" #include "chrome/browser/profiles/profile.h" +#include "components/optimization_guide/hints_processing_util.h" #include "components/optimization_guide/optimization_guide_features.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" -#include "components/previews/content/previews_hints_util.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" @@ -24,7 +24,8 @@ const std::string& host) { if (!top_host_blacklist) return false; - return top_host_blacklist->FindKey(previews::HashHostForDictionary(host)); + return top_host_blacklist->FindKey( + optimization_guide::HashHostForDictionary(host)); } } // namespace @@ -74,7 +75,8 @@ } if (detail.origin.SchemeIsHTTPOrHTTPS()) { top_host_blacklist->SetBoolKey( - HashHostForDictionary(detail.origin.host()), true); + optimization_guide::HashHostForDictionary(detail.origin.host()), + true); } } @@ -107,12 +109,12 @@ DictionaryPrefUpdate blacklist_pref(pref_service, kHintsFetcherTopHostBlacklist); - if (!blacklist_pref->FindKey(previews::HashHostForDictionary( + if (!blacklist_pref->FindKey(optimization_guide::HashHostForDictionary( navigation_handle->GetURL().host()))) { return; } - blacklist_pref->RemovePath( - previews::HashHostForDictionary(navigation_handle->GetURL().host())); + blacklist_pref->RemovePath(optimization_guide::HashHostForDictionary( + navigation_handle->GetURL().host())); if (blacklist_pref->empty()) { blacklist_pref->Clear(); pref_service->SetInteger(
diff --git a/chrome/browser/previews/previews_top_host_provider_impl_unittest.cc b/chrome/browser/previews/previews_top_host_provider_impl_unittest.cc index b3892b2..e3313cf 100644 --- a/chrome/browser/previews/previews_top_host_provider_impl_unittest.cc +++ b/chrome/browser/previews/previews_top_host_provider_impl_unittest.cc
@@ -9,10 +9,10 @@ #include "chrome/browser/engagement/site_engagement_service.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" +#include "components/optimization_guide/hints_processing_util.h" #include "components/optimization_guide/optimization_guide_features.h" #include "components/optimization_guide/optimization_guide_prefs.h" #include "components/prefs/pref_service.h" -#include "components/previews/content/previews_hints_util.h" #include "content/public/test/mock_navigation_handle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -47,7 +47,8 @@ const base::DictionaryValue* top_host_blacklist = pref_service_->GetDictionary( optimization_guide::prefs::kHintsFetcherTopHostBlacklist); - return top_host_blacklist->FindKey(previews::HashHostForDictionary(host)); + return top_host_blacklist->FindKey( + optimization_guide::HashHostForDictionary(host)); } void PopulateTopHostBlacklist(size_t num_hosts) { @@ -58,8 +59,9 @@ ->CreateDeepCopy(); for (size_t i = 1; i <= num_hosts; i++) { - top_host_filter->SetBoolKey( - HashHostForDictionary(base::StringPrintf("domain%zu.com", i)), true); + top_host_filter->SetBoolKey(optimization_guide::HashHostForDictionary( + base::StringPrintf("domain%zu.com", i)), + true); } pref_service_->Set(optimization_guide::prefs::kHintsFetcherTopHostBlacklist, *top_host_filter); @@ -71,7 +73,8 @@ ->GetDictionary( optimization_guide::prefs::kHintsFetcherTopHostBlacklist) ->CreateDeepCopy(); - top_host_filter->SetBoolKey(previews::HashHostForDictionary(host), true); + top_host_filter->SetBoolKey(optimization_guide::HashHostForDictionary(host), + true); pref_service_->Set(optimization_guide::prefs::kHintsFetcherTopHostBlacklist, *top_host_filter); } @@ -97,8 +100,8 @@ ->CreateDeepCopy(); for (size_t i = 1; i <= num_hosts_navigated; i++) { - top_host_filter->RemoveKey( - HashHostForDictionary(base::StringPrintf("domain%zu.com", i))); + top_host_filter->RemoveKey(optimization_guide::HashHostForDictionary( + base::StringPrintf("domain%zu.com", i))); } pref_service_->Set(optimization_guide::prefs::kHintsFetcherTopHostBlacklist, *top_host_filter);
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc index 327b868..27f62ba 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.cc +++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -12,7 +12,6 @@ #include "base/base64url.h" #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/feature_list.h" #include "base/logging.h" @@ -707,8 +706,8 @@ const PushMessagingAppIdentifier& app_identifier, RegisterCallback callback, const std::string& subscription_id, - const std::string& p256dh, - const std::string& auth_secret) { + std::string p256dh, + std::string auth_secret) { if (p256dh.empty()) { SubscribeEndWithError( std::move(callback), @@ -772,14 +771,14 @@ GetEncryptionInfoForAppId( app_id, sender_id, - base::Bind(&PushMessagingServiceImpl::DidGetEncryptionInfo, - weak_factory_.GetWeakPtr(), callback)); + base::BindOnce(&PushMessagingServiceImpl::DidGetEncryptionInfo, + weak_factory_.GetWeakPtr(), callback)); } void PushMessagingServiceImpl::DidGetEncryptionInfo( const SubscriptionInfoCallback& callback, - const std::string& p256dh, - const std::string& auth_secret) const { + std::string p256dh, + std::string auth_secret) const { // I/O errors might prevent the GCM Driver from retrieving a key-pair. bool is_valid = !p256dh.empty(); callback.Run(is_valid, std::vector<uint8_t>(p256dh.begin(), p256dh.end()), @@ -1127,11 +1126,9 @@ gcm::GCMEncryptionProvider::EncryptionInfoCallback callback) { if (PushMessagingAppIdentifier::UseInstanceID(app_id)) { GetInstanceIDDriver()->GetInstanceID(app_id)->GetEncryptionInfo( - NormalizeSenderInfo(sender_id), - base::AdaptCallbackForRepeating(std::move(callback))); + NormalizeSenderInfo(sender_id), std::move(callback)); } else { - GetGCMDriver()->GetEncryptionInfo( - app_id, base::AdaptCallbackForRepeating(std::move(callback))); + GetGCMDriver()->GetEncryptionInfo(app_id, std::move(callback)); } }
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h index 551508d6..6aa0c0f 100644 --- a/chrome/browser/push_messaging/push_messaging_service_impl.h +++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -190,8 +190,8 @@ const PushMessagingAppIdentifier& app_identifier, RegisterCallback callback, const std::string& subscription_id, - const std::string& p256dh, - const std::string& auth_secret); + std::string p256dh, + std::string auth_secret); // GetSubscriptionInfo methods ----------------------------------------------- @@ -201,8 +201,8 @@ bool is_valid); void DidGetEncryptionInfo(const SubscriptionInfoCallback& callback, - const std::string& p256dh, - const std::string& auth_secret) const; + std::string p256dh, + std::string auth_secret) const; // Unsubscribe methods -------------------------------------------------------
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html index cb3fd65..026de19 100644 --- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html +++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -167,16 +167,16 @@ sub-label="[[getPasswordState_(hasPin, prefs.settings.enable_screen_lock.value)]]"></cr-link-row> + <cr-link-row id="manage-other-people-subpage-trigger" + label="$i18n{manageOtherPeople}" on-click="onManageOtherPeople_"> + </cr-link-row> + <template is="dom-if" if="[[isKerberosEnabled_]]"> <cr-link-row id="kerberos-accounts-subpage-trigger" class="hr" on-click="onKerberosAccountsTap_" label="$i18n{kerberosAccountsSubMenuLabel}"></cr-link-row> </template> - <cr-link-row id="manage-other-people-subpage-trigger" - label="$i18n{manageOtherPeople}" on-click="onManageOtherPeople_"> - </cr-link-row> - </div> <template is="dom-if" route-path="/syncSetup" no-search="[[!isAdvancedSyncSettingsSearchable_(
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts.html b/chrome/browser/resources/settings/people_page/kerberos_accounts.html index 10ebe89..54a6b30 100644 --- a/chrome/browser/resources/settings/people_page/kerberos_accounts.html +++ b/chrome/browser/resources/settings/people_page/kerberos_accounts.html
@@ -17,7 +17,7 @@ <dom-module id="settings-kerberos-accounts"> <template> <style include="settings-shared iron-flex iron-flex-alignment"> - .profile-icon { + .account-icon { background: center / cover no-repeat; border-radius: 20px; flex-shrink: 0; @@ -34,7 +34,8 @@ padding-bottom: 1em; } - .account-policy-indicator { + .account-toolbar, + #remove-account-policy-indicator { margin-inline-start: 1em; } @@ -50,6 +51,11 @@ width: 24px; } + #remove-account-container { + align-items: center; + display: flex; + } + .error-badge { left: 60%; position: relative; @@ -66,7 +72,13 @@ } </style> - <div class="settings-box first">$i18n{kerberosAccountsDescription}</div> + <div class="settings-box first"> + <h2 class="first"> + <!-- Inner div needed to get spacing right in front of 'Learn more'. --> + <div inner-h-t-m-l="[[i18nAdvanced('kerberosAccountsDescription')]]"> + </div> + </h2> + </div> <div class="settings-box first"> <div id="account-list-header" class="flex"> @@ -88,7 +100,7 @@ <template is="dom-repeat" id="account-list" items="[[accounts_]]"> <div class="settings-box account-list-item"> - <div class="profile-icon" + <div class="account-icon" style="background-image: [[getIconImageSet_(item.pic)]]"> <img class="error-badge" hidden$="[[item.isSignedIn]]" src="chrome://resources/images/error_badge.svg"> @@ -98,22 +110,29 @@ <div class="flex text-elide"> <span>[[item.principalName]]</span> - <div class="secondary" hidden$="[[!item.isSignedIn]]"> - $i18n{kerberosAccountsSignedIn}[[getActiveLabel_(item.isActive)]] + <div class="secondary signed-in" hidden$="[[!item.isSignedIn]]"> + [[i18n('kerberosAccountsSignedIn', item.validForDuration)]] </div> - <div class="secondary warning" hidden$="[[item.isSignedIn]]"> - $i18n{kerberosAccountsSignedOut}[[getActiveLabel_(item.isActive)]] + <div class="secondary warning signed-out" + hidden$="[[item.isSignedIn]]"> + $i18n{kerberosAccountsSignedOut} </div> </div> </div> - <cr-button class="reauth-button" hidden$="[[item.isSignedIn]]" + <div class="secondary account-toolbar active-indicator" + hidden$="[[!item.isActive]]"> + $i18n{kerberosAccountsTicketActive} + </div> + + <cr-button class="account-toolbar reauth-button" + hidden$="[[item.isSignedIn]]" on-click="onReauthenticationClick_"> $i18n{kerberosAccountsReauthenticationLabel} </cr-button> <template is="dom-if" if="[[item.isManaged]]"> - <cr-policy-indicator class="account-policy-indicator" + <cr-policy-indicator class="account-toolbar account-policy-indicator" indicator-type="userPolicy"> </cr-policy-indicator> </template> @@ -137,11 +156,25 @@ </button> <button class="dropdown-item" on-click="onRemoveAccountClick_" disabled="[[selectedAccount_.isManaged]]"> - $i18n{kerberosAccountsRemoveAccountLabel} + <div id="remove-account-container"> + $i18n{kerberosAccountsRemoveAccountLabel} + <template is="dom-if" if="[[selectedAccount_.isManaged]]"> + <cr-policy-indicator id="remove-account-policy-indicator" + indicator-type="userPolicy"> + </cr-policy-indicator> + </template> + </div> </button> </cr-action-menu> </div> + <cr-toast id="account-removed-toast" duration="3000"> + <!-- Gets displayed with black font without div and id :-/ --> + <div id="account-removed-toast-label"> + $i18n{kerberosAccountsAccountRemovedTip} + </div> + </cr-toast> + <template is="dom-if" if="[[showAddAccountDialog_]]" restamp> <kerberos-add-account-dialog preset-account="[[selectedAccount_]]" on-close="onAddAccountDialogClosed_">
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts.js b/chrome/browser/resources/settings/people_page/kerberos_accounts.js index 3f063e2..aec55ed 100644 --- a/chrome/browser/resources/settings/people_page/kerberos_accounts.js +++ b/chrome/browser/resources/settings/people_page/kerberos_accounts.js
@@ -147,8 +147,16 @@ * @private */ onRemoveAccountClick_: function() { - this.browserProxy_.removeAccount( - /** @type {!settings.KerberosAccount} */ (this.selectedAccount_)); + this.browserProxy_ + .removeAccount( + /** @type {!settings.KerberosAccount} */ (this.selectedAccount_)) + .then(error => { + if (error == settings.KerberosErrorType.kNone) { + this.$$('#account-removed-toast').show(); + } else { + console.error('Unexpected error removing account: ' + error); + } + }); this.closeActionMenu_(); }, @@ -168,15 +176,5 @@ */ onRefreshNowClick_: function() { this.showAddAccountDialog_ = true; - }, - - /** - * @param {boolean} isActive Whether a Kerberos account is active. - * @return {string} Localized label for the active/inactive state. - * @private - */ - getActiveLabel_: function(isActive) { - // TODO(https://crbug.com/969850): Localize. - return isActive ? ', active' : ''; } });
diff --git a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js index 2720c29..b33aab6 100644 --- a/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js +++ b/chrome/browser/resources/settings/people_page/kerberos_accounts_browser_proxy.js
@@ -19,6 +19,7 @@ * isManaged: boolean, * passwordWasRemembered: boolean, * pic: string, + * validForDuration: string * }} */ settings.KerberosAccount; @@ -75,6 +76,7 @@ /** * Removes |account| from the set of Kerberos accounts. * @param {!settings.KerberosAccount} account + * @return {!Promise<!settings.KerberosErrorType>} */ removeAccount(account) {} @@ -105,7 +107,7 @@ /** @override */ removeAccount(account) { - chrome.send('removeKerberosAccount', [account.principalName]); + return cr.sendWithPromise('removeKerberosAccount', account.principalName); } /** @override */
diff --git a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html index cca2ea74..4a3b960 100644 --- a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html +++ b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.html
@@ -13,15 +13,25 @@ <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="../controls/settings_textarea.html"> +<link rel="import" href="../settings_shared_css.html"> <link rel="import" href="kerberos_accounts_browser_proxy.html"> <dom-module id="kerberos-add-account-dialog"> <template> - <style include="action-link"> + <style include="settings-shared action-link"> .label { @apply --cr-form-field-label; } + #advancedConfigDesc { + align-items: center; + display: flex; + } + + #advancedConfigPolicyIndicator { + margin-inline-start: 1em; + } + #credentials > *:not(:last-child) { margin-bottom: var(--cr-form-field-bottom-spacing); } @@ -30,25 +40,25 @@ height: 48px; } - #advancedConfigDesc, #rememberPasswordContainer { align-items: center; display: flex; margin-bottom: 32px; } - #advancedConfigDescLabel { - margin-inline-start: 1em; - } - #rememberPassword { margin-inline-end: 1em; } </style> <cr-dialog id="addDialog" hidden="[[showAdvancedConfig_]]"> - <div slot="title">$i18n{addKerberosAccount}</div> + <div slot="title">[[title_]]</div> + <div slot="body" spellcheck="false"> + <h2 class="start first"> + $i18n{addKerberosAccountDescription} + </h2> + <div id="general-error-container"> <div hidden="[[!showError_(generalErrorText_)]]"> <iron-icon id="general-error-icon" icon="cr:warning"></iron-icon> @@ -58,6 +68,7 @@ <div id="credentials"> <cr-input id="username" label="$i18n{kerberosUsername}" value="{{username_}}" invalid="[[showError_(usernameErrorText_)]]" + placeholder="user@example.com" error-message="[[usernameErrorText_]]"> </cr-input> @@ -82,17 +93,18 @@ <a is="action-link" id="advancedConfigButton" on-click="onAdvancedConfigClick_"> - $i18n{kerberosAdvancedConfigLabel} + $i18n{kerberosAccountsAdvancedConfigLabel} </a> </div> </div> + <div slot="button-container"> <cr-button class="cancel-button" on-click="onCancel_" id="cancel"> $i18n{cancel} </cr-button> <cr-button class="action-button" on-click="onAdd_" disabled="[[inProgress_]]"> - $i18n{add} + [[actionButtonLabel_]] </cr-button> </div> </cr-dialog> @@ -100,23 +112,22 @@ <template is="dom-if" if="[[showAdvancedConfig_]]" restamp> <cr-dialog id="advancedConfigDialog" on-close="onAdvancedConfigClose_"> <div slot="title">$i18n{kerberosAdvancedConfigTitle}</div> + <div slot="body"> - <div id="advancedConfigDesc"> + <h2 class="first" id="advancedConfigDesc"> + $i18n{kerberosAdvancedConfigDesc} <template is="dom-if" if="[[isManaged_]]"> <cr-policy-indicator id="advancedConfigPolicyIndicator" indicator-type="userPolicy"> </cr-policy-indicator> </template> - <div id="advancedConfigDescLabel"> - $i18n{kerberosAdvancedConfigDesc} - </div> - </div> + </h2> - <settings-textarea id="config" label="$i18n{kerberosConfig}" - value="{{editableConfig_}}" rows=12 spellcheck="false" - disabled="[[isManaged_]]"> + <settings-textarea id="config" value="{{editableConfig_}}" rows=12 + spellcheck="false" disabled="[[isManaged_]]"> </settings-textarea> </div> + <div slot="button-container"> <cr-button class="cancel-button" on-click="onAdvancedConfigCancel_">
diff --git a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js index b8e9147..c088633d 100644 --- a/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js +++ b/chrome/browser/resources/settings/people_page/kerberos_add_account_dialog.js
@@ -99,11 +99,22 @@ /** @private {string} */ config_: '', + /** @private {string} */ + title_: '', + + /** @private {string} */ + actionButtonLabel_: '', + /** @override */ attached: function() { this.$.addDialog.showModal(); if (this.presetAccount) { + // Refresh an existing account. + this.title_ = this.i18n('refreshKerberosAccount'); + this.actionButtonLabel_ = + this.i18n('addKerberosAccountRefreshButtonLabel'); + // Preset username and make UI read-only. // Note: At least the focus() part needs to be after showModal. this.username_ = this.presetAccount.principalName; @@ -126,6 +137,10 @@ this.config_ = this.presetAccount.config; } else { + // Add a new Kerberos account. + this.title_ = this.i18n('addKerberosAccount'); + this.actionButtonLabel_ = this.i18n('add'); + // Set a default configuration. this.config_ = loadTimeData.getString('defaultKerberosConfig'); }
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html index 4f37839..0163ea6d 100644 --- a/chrome/browser/resources/settings/people_page/people_page.html +++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -273,17 +273,17 @@ label="$i18n{accountManagerSubMenuLabel}"></cr-link-row> </template> + <cr-link-row id="manage-other-people-subpage-trigger" + label="$i18n{manageOtherPeople}" on-click="onManageOtherPeople_" + hidden="[[!pageVisibility.people.manageUsers]]"> + </cr-link-row> + <template is="dom-if" if="[[shouldShowKerberos_( isKerberosEnabled_, pageVisibility.people.kerberosAccounts)]]"> <cr-link-row id="kerberos-accounts-subpage-trigger" class="hr" on-click="onKerberosAccountsTap_" label="$i18n{kerberosAccountsSubMenuLabel}"></cr-link-row> </template> - - <cr-link-row id="manage-other-people-subpage-trigger" - label="$i18n{manageOtherPeople}" on-click="onManageOtherPeople_" - hidden="[[!pageVisibility.people.manageUsers]]"> - </cr-link-row> </if> <if expr="not chromeos">
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn index 3bfe772f..1a16628 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn +++ b/chrome/browser/safe_browsing/chrome_cleaner/BUILD.gn
@@ -63,6 +63,7 @@ "//chrome/installer/util:with_no_strings", "//components/chrome_cleaner/public/constants", "//components/chrome_cleaner/public/interfaces", + "//components/chrome_cleaner/public/proto", "//components/component_updater", "//components/crx_file", "//components/pref_registry", @@ -70,5 +71,6 @@ "//components/safe_browsing/common:safe_browsing_prefs", "//content/public/browser", "//extensions/browser", + "//third_party/protobuf:protobuf_lite", ] }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc index b6776362..d96df4e 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc
@@ -54,6 +54,8 @@ using CrashPoint = MockChromeCleanerProcess::CrashPoint; using ExtensionCleaningFeatureStatus = MockChromeCleanerProcess::ExtensionCleaningFeatureStatus; +using ProtobufIPCFeatureStatus = + MockChromeCleanerProcess::ProtobufIPCFeatureStatus; using IdleReason = ChromeCleanerController::IdleReason; using ItemsReporting = MockChromeCleanerProcess::ItemsReporting; using PromptAcceptance = ChromePromptActions::PromptAcceptance; @@ -230,6 +232,7 @@ CrashPoint, UwsFoundStatus, ExtensionCleaningFeatureStatus, + ProtobufIPCFeatureStatus, ItemsReporting, ItemsReporting, UserResponse> @@ -248,14 +251,24 @@ void SetUp() override { std::tie(process_status_, crash_point_, uws_found_status_, - extension_cleaning_feature_status_, registry_keys_reporting_, - extensions_reporting_, user_response_) = GetParam(); + extension_cleaning_feature_status_, protobuf_ipc_feature_status_, + registry_keys_reporting_, extensions_reporting_, user_response_) = + GetParam(); + std::vector<base::Feature> enabled_features; + std::vector<base::Feature> disabled_features; if (extension_cleaning_feature_status_ == - ExtensionCleaningFeatureStatus::kEnabled) - features_.InitAndEnableFeature(kChromeCleanupExtensionsFeature); - else - features_.InitAndDisableFeature(kChromeCleanupExtensionsFeature); + ExtensionCleaningFeatureStatus::kEnabled) { + enabled_features.push_back(kChromeCleanupExtensionsFeature); + } else { + disabled_features.push_back(kChromeCleanupExtensionsFeature); + } + if (protobuf_ipc_feature_status_ == ProtobufIPCFeatureStatus::kEnabled) { + enabled_features.push_back(kChromeCleanupProtobufIPCFeature); + } else { + disabled_features.push_back(kChromeCleanupProtobufIPCFeature); + } + features_.InitWithFeatures(enabled_features, disabled_features); InitializeEmptyExtensionService(); @@ -458,6 +471,7 @@ MockChromeCleanerProcess::CrashPoint crash_point_; UwsFoundStatus uws_found_status_; ExtensionCleaningFeatureStatus extension_cleaning_feature_status_; + ProtobufIPCFeatureStatus protobuf_ipc_feature_status_; ItemsReporting registry_keys_reporting_; ItemsReporting extensions_reporting_; ChromeCleanerController::UserResponse user_response_; @@ -636,6 +650,8 @@ UwsFoundStatus::kUwsFoundNoRebootRequired), Values(ExtensionCleaningFeatureStatus::kEnabled, ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(ItemsReporting::kUnsupported, ItemsReporting::kNotReported, ItemsReporting::kReported), @@ -655,6 +671,8 @@ ValuesIn(kCrashPointsAfterStartup), Values(UwsFoundStatus::kUwsFoundRebootRequired), Values(ExtensionCleaningFeatureStatus::kEnabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(ItemsReporting::kReported), Values(ItemsReporting::kReported), Values(UserResponse::kDenied, UserResponse::kDismissed)), @@ -671,19 +689,36 @@ ValuesIn(kCrashPointsAfterStartup), Values(UwsFoundStatus::kNoUwsFound), Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(ItemsReporting::kNotReported), Values(ItemsReporting::kNotReported), Values(UserResponse::kDismissed)), chrome_cleaner::GetParamNameForTest()); +// Tests where the process fails to download. This never gets far enough to +// collect results, so we can save time by not repeating the tests for every +// possible combination of results parameters. +INSTANTIATE_TEST_SUITE_P( + CleanerFailsToDownload, + ChromeCleanerControllerTest, + Combine(Values(CleanerProcessStatus::kFetchFailure), + Values(CrashPoint::kNone), + Values(UwsFoundStatus::kNoUwsFound), + Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kDisabled), + Values(ItemsReporting::kUnsupported), + Values(ItemsReporting::kUnsupported), + Values(UserResponse::kAcceptedWithLogs)), + chrome_cleaner::GetParamNameForTest()); + // Tests where the process fails before starting a scan. This never gets far // enough to collect results, so we can save time by not repeating the tests // for every possible combination of results parameters. INSTANTIATE_TEST_SUITE_P( CleanerFailsToStart, ChromeCleanerControllerTest, - Combine(Values(CleanerProcessStatus::kFetchFailure, - CleanerProcessStatus::kFetchSuccessInvalidProcess, + Combine(Values(CleanerProcessStatus::kFetchSuccessInvalidProcess, CleanerProcessStatus::kFetchSuccessValidProcess), // If the process status is kFetchSuccessValidProcess, crash on // startup. Otherwise the process never starts so the crash point @@ -691,6 +726,8 @@ Values(CrashPoint::kOnStartup), Values(UwsFoundStatus::kNoUwsFound), Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(ItemsReporting::kUnsupported), Values(ItemsReporting::kUnsupported), Values(UserResponse::kAcceptedWithLogs)),
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc index 96c0584..cef0ecc 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -41,6 +41,8 @@ using ChromeMetricsStatus = ChromeCleanerRunner::ChromeMetricsStatus; using ExtensionCleaningFeatureStatus = MockChromeCleanerProcess::ExtensionCleaningFeatureStatus; +using ProtobufIPCFeatureStatus = + MockChromeCleanerProcess::ProtobufIPCFeatureStatus; using PromptAcceptance = ChromePromptActions::PromptAcceptance; using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus; @@ -211,6 +213,7 @@ typedef std::tuple<UwsFoundStatus, ExtensionCleaningFeatureStatus, + ProtobufIPCFeatureStatus, MockChromeCleanerProcess::ItemsReporting, MockChromeCleanerProcess::ItemsReporting, MockChromeCleanerProcess::CrashPoint, @@ -239,17 +242,27 @@ MockChromeCleanerProcess::ItemsReporting extensions_reporting; MockChromeCleanerProcess::CrashPoint crash_point; std::tie(uws_found_state, extension_cleaning_feature_status_, - registry_keys_reporting, extensions_reporting, crash_point, - prompt_acceptance_to_send_) = GetParam(); + protobuf_ipc_feature_status_, registry_keys_reporting, + extensions_reporting, crash_point, prompt_acceptance_to_send_) = + GetParam(); ASSERT_FALSE(uws_found_state == UwsFoundStatus::kNoUwsFound && prompt_acceptance_to_send_ != PromptAcceptance::DENIED); + std::vector<base::Feature> enabled_features; + std::vector<base::Feature> disabled_features; if (extension_cleaning_feature_status_ == - ExtensionCleaningFeatureStatus::kEnabled) - features_.InitAndEnableFeature(kChromeCleanupExtensionsFeature); - else - features_.InitAndDisableFeature(kChromeCleanupExtensionsFeature); + ExtensionCleaningFeatureStatus::kEnabled) { + enabled_features.push_back(kChromeCleanupExtensionsFeature); + } else { + disabled_features.push_back(kChromeCleanupExtensionsFeature); + } + if (protobuf_ipc_feature_status_ == ProtobufIPCFeatureStatus::kEnabled) { + enabled_features.push_back(kChromeCleanupProtobufIPCFeature); + } else { + disabled_features.push_back(kChromeCleanupProtobufIPCFeature); + } + features_.InitWithFeatures(enabled_features, disabled_features); cleaner_process_options_.SetReportedResults( uws_found_state != UwsFoundStatus::kNoUwsFound, registry_keys_reporting, @@ -340,6 +353,7 @@ MockChromeCleanerProcess::Options cleaner_process_options_; PromptAcceptance prompt_acceptance_to_send_ = PromptAcceptance::UNSPECIFIED; ExtensionCleaningFeatureStatus extension_cleaning_feature_status_; + ProtobufIPCFeatureStatus protobuf_ipc_feature_status_; // Set by OnProcessDone(). ChromeCleanerRunner::ProcessStatus process_status_; @@ -434,6 +448,8 @@ Combine(Values(UwsFoundStatus::kNoUwsFound), // When no UwS is found we don't care about extension removel. Values(ExtensionCleaningFeatureStatus::kDisabled), + // When no UwS is found there is no prompt. + Values(ProtobufIPCFeatureStatus::kDisabled), Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, MockChromeCleanerProcess::ItemsReporting::kNotReported, MockChromeCleanerProcess::ItemsReporting::kReported), @@ -451,6 +467,8 @@ Values(UwsFoundStatus::kNoUwsFound), // When no UwS is found we don't care about extension removel. Values(ExtensionCleaningFeatureStatus::kDisabled), + // When no UwS is found there is no prompt. + Values(ProtobufIPCFeatureStatus::kDisabled), Values(MockChromeCleanerProcess::ItemsReporting::kReported), Values(MockChromeCleanerProcess::ItemsReporting::kReported), Values(MockChromeCleanerProcess::CrashPoint::kOnStartup, @@ -467,6 +485,8 @@ UwsFoundStatus::kUwsFoundNoRebootRequired), Values(ExtensionCleaningFeatureStatus::kEnabled, ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(MockChromeCleanerProcess::ItemsReporting::kUnsupported, MockChromeCleanerProcess::ItemsReporting::kNotReported, MockChromeCleanerProcess::ItemsReporting::kReported), @@ -485,6 +505,8 @@ Combine( Values(UwsFoundStatus::kUwsFoundRebootRequired), Values(ExtensionCleaningFeatureStatus::kDisabled), + Values(ProtobufIPCFeatureStatus::kEnabled, + ProtobufIPCFeatureStatus::kDisabled), Values(MockChromeCleanerProcess::ItemsReporting::kReported), Values(MockChromeCleanerProcess::ItemsReporting::kReported), Values(MockChromeCleanerProcess::CrashPoint::kOnStartup,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc index e0f3d35..111d559 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.cc
@@ -27,8 +27,8 @@ #include "base/task/post_task.h" #include "base/unguessable_token.h" #include "base/win/win_util.h" -#include "chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h" #include "components/chrome_cleaner/public/constants/constants.h" +#include "components/chrome_cleaner/public/constants/result_codes.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/extension_system.h" @@ -37,6 +37,7 @@ namespace safe_browsing { using base::win::ScopedHandle; +using chrome_cleaner::ChromePromptRequest; using content::BrowserThread; using CleanerProcessDelegate = ChromePromptChannel::CleanerProcessDelegate; @@ -141,13 +142,59 @@ switch_string, base::NumberToString(base::win::HandleToUint32(handle))); } +// Reads |buffer_size| bytes from a pipe of PIPE_TYPE_MESSAGE. +bool ReadMessageFromPipe(HANDLE handle, LPVOID buffer, DWORD buffer_size) { + // If the process at the other end of the pipe is behaving correctly it will + // write exactly |buffer_size| bytes to the pipe and PIPE_TYPE_MESSAGE + // ensures that we read all of them at once. If the process writes more bytes + // than expected, ::ReadFile will return ERROR_MORE_DATA. If it writes less + // we treat that as an error. + DWORD bytes_read = 0; + if (!::ReadFile(handle, buffer, buffer_size, &bytes_read, nullptr)) { + PLOG(ERROR) << "ReadFile failed"; + return false; + } + CHECK_LE(bytes_read, buffer_size); + if (bytes_read != buffer_size) { + PLOG(ERROR) << "Short read (read " << bytes_read << " of " << buffer_size + << ")"; + return false; + } + return true; +} + +// Loop doing blocking reads from the IPC pipe with |request_read_handle| until +// a CloseConnection message is received or an error occurs. For each message +// deserialized from the pipe a handler method on |channel| will be called. +// +// |request_read_handle| is owned by |channel|, which is never deleted until +// after the cleaner process at the other end of the pipe exits. +// +// This should be invoked from a CONTINUE_ON_SHUTDOWN task so that it does not +// block Chrome shutdown. +// +// Exit conditions: +// * If the cleaner process sends a CloseConnection message the loop will exit +// cleanly. +// * If the cleaner process exits without sending a CloseConnection message, +// the next ::ReadFile call will return ERROR_BROKEN_PIPE. +// * If |channel| is deleted or channel->CloseHandles is called, the next +// ::ReadFile call will return ERROR_INVALID_HANDLE. +// +// Important: it is not safe to dereference |channel| on this sequence. Always +// post to |task_runner| instead. Since |channel| is deleted on |task_runner|, +// dereferencing the weak pointer on any other sequence is racy. +// +// When done, calls |on_connection_closed|. On error also slays +// |cleaner_process|, which is the other end of the pipe. void ServiceChromePromptRequests( base::WeakPtr<ChromePromptChannelProtobuf> channel, scoped_refptr<base::SequencedTaskRunner> task_runner, HANDLE request_read_handle, - HANDLE response_write_handle, std::unique_ptr<CleanerProcessDelegate> cleaner_process, base::OnceClosure on_connection_closed) { + static constexpr uint32_t kMaxMessageLength = 1 * 1024 * 1024; // 1M bytes + // Always call OnConnectionClosed when finished whether it's with an error or // because a CloseConnectionRequest was received. base::ScopedClosureRunner call_connection_closed( @@ -158,13 +205,89 @@ base::ScopedClosureRunner kill_cleaner_on_error(base::BindOnce( &CleanerProcessDelegate::TerminateOnError, std::move(cleaner_process))); - // TODO(crbug.com/969139): Read requests and send responses. Any errors that - // happen during this stage will call |kill_cleaner_on_error|. It will never - // be called until this TODO is implemented. + // Wait for the cleaner to write a single version byte and make sure it's + // using a recognized version. + DWORD bytes_read = 0; + uint8_t version = 0; + // Since the field is only 1 byte it's not possible to have a short read. + static_assert(sizeof(version) == 1, + "version field must be readable in 1 byte"); + if (!::ReadFile(request_read_handle, &version, sizeof(version), &bytes_read, + nullptr)) { + PLOG(ERROR) << "Failed to read protocol version"; + return; + } + CHECK_EQ(bytes_read, sizeof(version)); + if (version != 1) { + LOG(ERROR) << "Cleaner requested unsupported version " << version; + return; + } - // Normal exit: do not kill the cleaner. OnConnectionClosed will still be - // called. - kill_cleaner_on_error.ReplaceClosure(base::DoNothing()); + // Repeatedly wait for requests from the cleaner. + while (true) { + // Read the request length followed by a request. + uint32_t request_length = 0; + if (!ReadMessageFromPipe(request_read_handle, &request_length, + sizeof(request_length))) { + return; + } + if (request_length < 1 || request_length > kMaxMessageLength) { + PLOG(ERROR) << "Bad request length: " << request_length; + return; + } + std::string request; + // Include space for the null terminator in the WriteInto call. + if (!ReadMessageFromPipe(request_read_handle, + base::WriteInto(&request, request_length + 1), + request_length)) { + return; + } + + ChromePromptRequest chrome_prompt_request; + if (!chrome_prompt_request.ParseFromString(request)) { + PLOG(ERROR) << "Read invalid message"; + return; + } + switch (chrome_prompt_request.request_case()) { + case ChromePromptRequest::kQueryCapability: + task_runner->PostTask( + FROM_HERE, + base::BindOnce( + &ChromePromptChannelProtobuf::HandleQueryCapabilityRequest, + channel, chrome_prompt_request.query_capability())); + break; + case ChromePromptRequest::kPromptUser: + task_runner->PostTask( + FROM_HERE, + base::BindOnce( + &ChromePromptChannelProtobuf::HandlePromptUserRequest, channel, + chrome_prompt_request.prompt_user())); + break; + case ChromePromptRequest::kRemoveExtensions: + task_runner->PostTask( + FROM_HERE, + base::BindOnce( + &ChromePromptChannelProtobuf::HandleRemoveExtensionsRequest, + channel, chrome_prompt_request.remove_extensions())); + break; + case ChromePromptRequest::kCloseConnection: { + // Normal exit: do not kill the cleaner. OnConnectionClosed will still + // be called. + kill_cleaner_on_error.ReplaceClosure(base::DoNothing()); + + // The cleaner process will continue running but the pipe handles are + // no longer needed. + task_runner->PostTask( + FROM_HERE, + base::BindOnce(&ChromePromptChannelProtobuf::CloseHandles, + channel)); + return; + } + default: + PLOG(ERROR) << "Read unknown request"; + return; + } + } } } // namespace @@ -370,16 +493,184 @@ // methods on |task_runner_|. // // This object continues to own the pipe handles. ServiceChromePromptRequests - // gets raw handles, which can go invalid at any time either because the + // gets a raw handle, which can go invalid at any time either because the // other end of the pipe closes or CloseHandles is called. When that happens // the next call to ::ReadFile will return an error and // ServiceChromePromptRequests will return. base::PostTask( - FROM_HERE, {base::MayBlock()}, + FROM_HERE, + {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::BindOnce(&ServiceChromePromptRequests, weak_factory_.GetWeakPtr(), task_runner_, request_read_handle_.Get(), - response_write_handle_.Get(), std::move(cleaner_process), + std::move(cleaner_process), std::move(on_connection_closed_))); } +void ChromePromptChannelProtobuf::WriteResponseMessage( + const google::protobuf::MessageLite& message) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + base::ScopedClosureRunner error_handler(base::BindOnce( + &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this))); + + std::string response_string; + if (!message.SerializeToString(&response_string)) { + LOG(ERROR) << "Failed to serialize response message"; + return; + } + + DWORD bytes_written = 0; + uint32_t message_size = response_string.size(); + if (!::WriteFile(response_write_handle_.Get(), &message_size, + sizeof(uint32_t), &bytes_written, nullptr)) { + PLOG(ERROR) << "Failed to write message size"; + return; + } + CHECK_EQ(bytes_written, sizeof(uint32_t)); + if (!::WriteFile(response_write_handle_.Get(), response_string.data(), + message_size, &bytes_written, nullptr)) { + PLOG(ERROR) << "Failed to write message of length " << message_size; + return; + } + CHECK_EQ(bytes_written, message_size); + + // No error occurred. + error_handler.ReplaceClosure(base::DoNothing()); +} + +void ChromePromptChannelProtobuf::CloseHandles() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // This will cause the next ::ReadFile call in ServiceChromePromptRequests to + // fail, triggering the error handler that kills the cleaner process. + request_read_handle_.Close(); + response_write_handle_.Close(); +} + +void ChromePromptChannelProtobuf::HandleQueryCapabilityRequest( + const chrome_cleaner::QueryCapabilityRequest&) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // No optional capabilities are supported. Send back an empty response. + WriteResponseMessage(chrome_cleaner::QueryCapabilityResponse()); +} + +void ChromePromptChannelProtobuf::HandlePromptUserRequest( + const chrome_cleaner::PromptUserRequest& request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + base::ScopedClosureRunner error_handler(base::BindOnce( + &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this))); + + // If there are any fields we don't know how to display, do not prompt. (Not + // an error, could just be a more recent cleaner version.) + if (!request.unknown_fields().empty()) { + LOG(ERROR) << "Discarding PromptUserRequest with unknown fields."; + return; + } + + std::vector<base::FilePath> files_to_delete; + files_to_delete.reserve(request.files_to_delete_size()); + for (const std::string& file_path : request.files_to_delete()) { + base::string16 file_path_utf16; + if (!base::UTF8ToUTF16(file_path.c_str(), file_path.size(), + &file_path_utf16)) { + LOG(ERROR) << "Undisplayable file path in PromptUserRequest."; + return; + } + files_to_delete.push_back(base::FilePath(file_path_utf16)); + } + + base::Optional<std::vector<base::string16>> optional_registry_keys; + if (request.registry_keys_size()) { + std::vector<base::string16> registry_keys; + registry_keys.reserve(request.registry_keys_size()); + for (const std::string& registry_key : request.registry_keys()) { + base::string16 registry_key_utf16; + if (!base::UTF8ToUTF16(registry_key.c_str(), registry_key.size(), + ®istry_key_utf16)) { + LOG(ERROR) << "Undisplayable registry key in PromptUserRequest."; + return; + } + registry_keys.push_back(registry_key_utf16); + } + optional_registry_keys = registry_keys; + } + + base::Optional<std::vector<base::string16>> optional_extension_ids; + if (request.extension_ids_size()) { + std::vector<base::string16> extension_ids; + extension_ids.reserve(request.extension_ids_size()); + for (const std::string& extension_id : request.extension_ids()) { + base::string16 extension_id_utf16; + // TODO(crbug.com/969139): change ChromePromptActions to use strings for + // extension_id and skip this conversion. + if (!base::UTF8ToUTF16(extension_id.c_str(), extension_id.size(), + &extension_id_utf16)) { + LOG(ERROR) << "Undisplayable extension id in PromptUserRequest."; + return; + } + extension_ids.push_back(extension_id_utf16); + } + optional_extension_ids = extension_ids; + } + + // No error occurred. + error_handler.ReplaceClosure(base::DoNothing()); + + // Ensure SendPromptUserResponse runs on this sequence. + auto response_callback = base::BindOnce( + [](scoped_refptr<base::SequencedTaskRunner> task_runner, + base::WeakPtr<ChromePromptChannelProtobuf> channel, + ChromePromptActions::PromptAcceptance acceptance) { + task_runner->PostTask( + FROM_HERE, + base::BindOnce(&ChromePromptChannelProtobuf::SendPromptUserResponse, + channel, acceptance)); + }, + task_runner_, weak_factory_.GetWeakPtr()); + actions_->PromptUser(files_to_delete, optional_registry_keys, + optional_extension_ids, std::move(response_callback)); +} + +void ChromePromptChannelProtobuf::HandleRemoveExtensionsRequest( + const chrome_cleaner::RemoveExtensionsRequest& request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + base::ScopedClosureRunner error_handler(base::BindOnce( + &ChromePromptChannelProtobuf::CloseHandles, base::Unretained(this))); + + // extension_ids are mandatory. + if (!request.extension_ids_size()) { + LOG(ERROR) << "Bad RemoveExtensionsRequest"; + return; + } + + std::vector<base::string16> extension_ids; + extension_ids.reserve(request.extension_ids_size()); + for (const std::string& extension_id : request.extension_ids()) { + base::string16 extension_id_utf16; + // TODO(crbug.com/969139): change ChromePromptActions to use strings for + // extension_id and skip this conversion. + if (!base::UTF8ToUTF16(extension_id.c_str(), extension_id.size(), + &extension_id_utf16)) { + LOG(ERROR) << "Unusable extension id in RemoveExtensionsReqest."; + return; + } + extension_ids.push_back(extension_id_utf16); + } + + // No error occurred. + error_handler.ReplaceClosure(base::DoNothing()); + + chrome_cleaner::RemoveExtensionsResponse response; + response.set_success(actions_->DisableExtensions(extension_ids)); + WriteResponseMessage(response); +} + +void ChromePromptChannelProtobuf::SendPromptUserResponse( + ChromePromptActions::PromptAcceptance acceptance) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + chrome_cleaner::PromptUserResponse response; + response.set_prompt_acceptance( + static_cast<chrome_cleaner::PromptUserResponse::PromptAcceptance>( + acceptance)); + WriteResponseMessage(response); +} + } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h index 0b673c2..5c82c400 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h +++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_channel_win.h
@@ -15,11 +15,13 @@ #include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" #include "base/win/scoped_handle.h" +#include "chrome/browser/safe_browsing/chrome_cleaner/chrome_prompt_actions_win.h" #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h" -#include "content/public/browser/browser_thread.h" +#include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/invitation.h" #include "mojo/public/cpp/system/message_pipe.h" +#include "third_party/protobuf/src/google/protobuf/message_lite.h" namespace base { class CommandLine; @@ -146,12 +148,35 @@ void ConnectToCleaner( std::unique_ptr<CleanerProcessDelegate> cleaner_process) override; + // Handles |request| and sends a QueryCapabilityResponse in reply. + void HandleQueryCapabilityRequest( + const chrome_cleaner::QueryCapabilityRequest& request); + + // Handles |request| and sends a PromptUserResponse in reply. + void HandlePromptUserRequest( + const chrome_cleaner::PromptUserRequest& request); + + // Handles |request| and sends a RemoveExtensionsResponse in reply. + void HandleRemoveExtensionsRequest( + const chrome_cleaner::RemoveExtensionsRequest& request); + + // Closes request_read_handle_ and request_write_handle_, which will trigger + // an error handler in ServiceChromePromptRequests. + void CloseHandles(); + private: ChromePromptChannelProtobuf(const ChromePromptChannelProtobuf& other) = delete; ChromePromptChannelProtobuf& operator=( const ChromePromptChannelProtobuf& other) = delete; + // Serializes |message| to response_write_handle_. Calls CloseHandles on + // error. + void WriteResponseMessage(const google::protobuf::MessageLite& message); + + // Sends a PromptUserResponse with the given |acceptance| value. + void SendPromptUserResponse(ChromePromptActions::PromptAcceptance acceptance); + SEQUENCE_CHECKER(sequence_checker_); // Requests always flow from the Chrome Cleanup tool to Chrome.
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc index 5abc05b..44c33ca 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc +++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
@@ -31,6 +31,7 @@ #include "chrome/grit/generated_resources.h" #include "components/chrome_cleaner/public/constants/constants.h" #include "components/chrome_cleaner/public/interfaces/chrome_prompt.mojom.h" +#include "components/chrome_cleaner/public/proto/chrome_prompt.pb.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "extensions/common/manifest.h" @@ -50,8 +51,6 @@ using ::chrome_cleaner::mojom::ChromePromptPtrInfo; using mojo::core::ScopedIPCSupport; using CrashPoint = MockChromeCleanerProcess::CrashPoint; -using ExtensionCleaningFeatureStatus = - MockChromeCleanerProcess::ExtensionCleaningFeatureStatus; using ItemsReporting = MockChromeCleanerProcess::ItemsReporting; using PromptAcceptance = ChromePromptActions::PromptAcceptance; using UwsFoundStatus = MockChromeCleanerProcess::UwsFoundStatus; @@ -195,19 +194,106 @@ ~MockCleanerResultsProtobuf() override = default; void SendScanResults(base::OnceClosure done_closure) override { + base::ScopedClosureRunner call_done_closure(std::move(done_closure)); if (!read_handle_.IsValid() || !write_handle_.IsValid()) { LOG(ERROR) << "IPC pipes were not connected correctly"; - std::move(done_closure).Run(); return; } // TODO(crbug.com/969139): Populate a request proto based on |options_| and // send it. + + // Send the protocol version number. + DWORD bytes_written = 0; + static const uint8_t kVersion = 1; + if (!::WriteFile(write_handle_.Get(), &kVersion, sizeof(kVersion), + &bytes_written, nullptr)) { + PLOG(ERROR) << "Error writing protocol version"; + return; + } + + // Send a PromptUser request. + chrome_cleaner::ChromePromptRequest request; + chrome_cleaner::PromptUserRequest* prompt_user = + request.mutable_prompt_user(); + for (const base::FilePath& file : options_.files_to_delete()) { + prompt_user->add_files_to_delete(file.AsUTF8Unsafe()); + } + if (options_.registry_keys().has_value()) { + for (const base::string16& key : options_.registry_keys().value()) { + prompt_user->add_registry_keys(base::UTF16ToUTF8(key)); + } + } + if (options_.extension_ids().has_value()) { + for (const base::string16& id : options_.extension_ids().value()) { + prompt_user->add_extension_ids(base::UTF16ToUTF8(id)); + } + } + if (!WriteMessage(request.SerializeAsString())) + return; + if (options_.crash_point() == CrashPoint::kAfterRequestSent) { ::exit(MockChromeCleanerProcess::kDeliberateCrashExitCode); } + + // Wait for the response. + std::string response_message = ReadResponse(); + if (response_message.empty()) + return; + chrome_cleaner::PromptUserResponse response; + if (!response.ParseFromString(response_message)) { + LOG(ERROR) << "Read invalid PromptUser response: " << response_message; + return; + } + ReceivePromptAcceptance( + base::BindOnce(&MockCleanerResultsProtobuf::SendCloseConnectionRequest, + base::Unretained(this), call_done_closure.Release()), + static_cast<PromptAcceptance>(response.prompt_acceptance())); + } + + void SendCloseConnectionRequest(base::OnceClosure done_closure) { + chrome_cleaner::ChromePromptRequest request; + // Initialize a CloseConnectionRequest + request.mutable_close_connection(); + WriteMessage(request.SerializeAsString()); + std::move(done_closure).Run(); } private: + bool WriteMessage(const std::string& message) { + uint32_t message_length = message.size(); + DWORD bytes_written = 0; + if (!::WriteFile(write_handle_.Get(), &message_length, + sizeof(message_length), &bytes_written, nullptr)) { + PLOG(ERROR) << "Error writing message length"; + return false; + } + if (!::WriteFile(write_handle_.Get(), message.c_str(), message_length, + &bytes_written, nullptr)) { + PLOG(ERROR) << "Error writing message"; + return false; + } + return true; + } + + std::string ReadResponse() { + uint32_t response_length = 0; + DWORD bytes_read = 0; + // Include space for the null terminator in the WriteInto call. + if (!::ReadFile(read_handle_.Get(), &response_length, + sizeof(response_length), &bytes_read, nullptr)) { + PLOG(ERROR) << "Error reading response length"; + return std::string(); + } + std::string response_message; + if (!::ReadFile(read_handle_.Get(), + base::WriteInto(&response_message, response_length + 1), + response_length, &bytes_read, nullptr)) { + PLOG(ERROR) << "Error reading response message"; + return std::string(); + } + return response_message; + } + base::win::ScopedHandle read_handle_; base::win::ScopedHandle write_handle_; }; @@ -570,11 +656,18 @@ return out << "UwS" << static_cast<int>(status); } -std::ostream& operator<<(std::ostream& out, - ExtensionCleaningFeatureStatus status) { +std::ostream& operator<<( + std::ostream& out, + MockChromeCleanerProcess::ExtensionCleaningFeatureStatus status) { return out << "Ext" << static_cast<int>(status); } +std::ostream& operator<<( + std::ostream& out, + MockChromeCleanerProcess::ProtobufIPCFeatureStatus status) { + return out << "Ipc" << static_cast<int>(status); +} + std::ostream& operator<<(std::ostream& out, ItemsReporting items_reporting) { return out << "Items" << static_cast<int>(items_reporting); }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h index 9ae2dd2..b70e643 100644 --- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h +++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.h
@@ -70,6 +70,11 @@ kDisabled, }; + enum class ProtobufIPCFeatureStatus { + kEnabled, + kDisabled, + }; + static constexpr int kInternalTestFailureExitCode = 100001; static constexpr int kDeliberateCrashExitCode = 100002; static constexpr int kNothingFoundExitCode = 2; @@ -191,6 +196,10 @@ std::ostream& operator<<( std::ostream& out, + MockChromeCleanerProcess::ProtobufIPCFeatureStatus status); + +std::ostream& operator<<( + std::ostream& out, MockChromeCleanerProcess::ItemsReporting items_reporting); } // namespace safe_browsing
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc index 0457b1f..97d6998a 100644 --- a/chrome/browser/search/instant_service.cc +++ b/chrome/browser/search/instant_service.cc
@@ -190,6 +190,7 @@ pref_service_(profile_->GetPrefs()), theme_observer_(this), native_theme_(ui::NativeTheme::GetInstanceForNativeUi()), + background_updated_timestamp_(base::TimeTicks::Now()), weak_ptr_factory_(this) { // The initialization below depends on a typical set of browser threads. Skip // it if we are running in a unit test without the full suite. @@ -441,12 +442,14 @@ pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false); RemoveLocalBackgroundImageCopy(); + background_updated_timestamp_ = base::TimeTicks::Now(); + if (background_url.is_valid() && is_backdrop_url) { const GURL& thumbnail_url = background_service_->GetThumbnailUrl(background_url); - FetchCustomBackground(background_url, thumbnail_url.is_valid() - ? thumbnail_url - : background_url); + FetchCustomBackground( + background_updated_timestamp_, + thumbnail_url.is_valid() ? thumbnail_url : background_url); base::DictionaryValue background_info = GetBackgroundInfoAsDict( background_url, attribution_line_1, attribution_line_2, action_url); @@ -464,6 +467,7 @@ } void InstantService::SetBackgroundToLocalResource() { + background_updated_timestamp_ = base::TimeTicks::Now(); pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, true); UpdateThemeInfo(); } @@ -796,7 +800,7 @@ } void InstantService::UpdateCustomBackgroundColorAsync( - const GURL& image_url, + base::TimeTicks timestamp, const gfx::Image& fetched_image, const image_fetcher::RequestMetadata& metadata) { // Calculate the bitmap color asynchronously as it is slow (1-2 seconds for @@ -806,11 +810,11 @@ FROM_HERE, {base::TaskPriority::BEST_EFFORT}, base::BindOnce(&GetBitmapMainColor, *fetched_image.ToSkBitmap()), base::BindOnce(&InstantService::UpdateCustomBackgroundPrefsWithColor, - weak_ptr_factory_.GetWeakPtr(), image_url)); + weak_ptr_factory_.GetWeakPtr(), timestamp)); } } -void InstantService::FetchCustomBackground(const GURL& image_url, +void InstantService::FetchCustomBackground(base::TimeTicks timestamp, const GURL& fetch_url) { DCHECK(!fetch_url.is_empty()); @@ -837,9 +841,9 @@ image_fetcher::ImageFetcherParams params(traffic_annotation, kCustomBackgroundsUmaClientName); image_fetcher_->FetchImage( - image_url, + fetch_url, base::BindOnce(&InstantService::UpdateCustomBackgroundColorAsync, - weak_ptr_factory_.GetWeakPtr(), image_url), + weak_ptr_factory_.GetWeakPtr(), timestamp), std::move(params)); } @@ -881,8 +885,9 @@ registry->RegisterBooleanPref(prefs::kNtpShortcutsVisible, true); } -void InstantService::UpdateCustomBackgroundPrefsWithColor(const GURL& image_url, - SkColor color) { +void InstantService::UpdateCustomBackgroundPrefsWithColor( + base::TimeTicks timestamp, + SkColor color) { // Update background color only if the selected background is still the same. const base::DictionaryValue* background_info = pref_service_->GetDictionary(prefs::kNtpCustomBackgroundDict); @@ -891,7 +896,7 @@ GURL current_bg_url( background_info->FindKey(kNtpCustomBackgroundURL)->GetString()); - if (current_bg_url == image_url) { + if (timestamp == background_updated_timestamp_) { pref_service_->Set(prefs::kNtpCustomBackgroundDict, GetBackgroundInfoWithColor(background_info, color)); }
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h index 2a73342..35e6132 100644 --- a/chrome/browser/search/instant_service.h +++ b/chrome/browser/search/instant_service.h
@@ -162,12 +162,12 @@ // Calculates the most frequent color of the image and stores it in prefs. void UpdateCustomBackgroundColorAsync( - const GURL& image_url, + base::TimeTicks timestamp, const gfx::Image& fetched_image, const image_fetcher::RequestMetadata& metadata); // Fetches the image for the given |fetch_url|. - void FetchCustomBackground(const GURL& image_url, const GURL& fetch_url); + void FetchCustomBackground(base::TimeTicks timestamp, const GURL& fetch_url); private: class SearchProviderObserver; @@ -184,6 +184,9 @@ FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, DoesToggleShortcutsVisibility); FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, IsCustomLinksEnabled); FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, TestNoThemeInfo); + FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, TestUpdateCustomBackgroundColor); + FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, + LocalImageDoesNotUpdateCustomBackgroundColor); // KeyedService: void Shutdown() override; @@ -237,12 +240,17 @@ // chrome-search://local-ntp/background.jpg void SetBackgroundToLocalResource(); - // Updates custom background prefs with color for the given |image_url|. - void UpdateCustomBackgroundPrefsWithColor(const GURL& image_url, + // Updates custom background prefs with color if the background hasn't changed + // since the calculation started. + void UpdateCustomBackgroundPrefsWithColor(base::TimeTicks timestamp, SkColor color); void SetImageFetcherForTesting(image_fetcher::ImageFetcher* image_fetcher); + base::TimeTicks GetBackgroundUpdatedTimestampForTesting() { + return background_updated_timestamp_; + } + Profile* const profile_; // The process ids associated with Instant processes. @@ -279,6 +287,8 @@ std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_; + base::TimeTicks background_updated_timestamp_; + base::WeakPtrFactory<InstantService> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(InstantService);
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc index 610fad2..fc86ac3 100644 --- a/chrome/browser/search/instant_service_unittest.cc +++ b/chrome/browser/search/instant_service_unittest.cc
@@ -581,7 +581,7 @@ // Background color will not update if no background is set. instant_service_->UpdateCustomBackgroundColorAsync( - GURL(), image, image_fetcher::RequestMetadata()); + base::TimeTicks::Now(), image, image_fetcher::RequestMetadata()); thread_bundle()->RunUntilIdle(); EXPECT_FALSE(CheckBackgroundColor( SK_ColorRED, @@ -597,9 +597,9 @@ instant_service_->SetCustomBackgroundURLWithAttributions( kUrl, kAttributionLine1, kAttributionLine2, kActionUrl); - // Background color will not update if current background url changed. + // Background color will not update if background timestamp has changed. instant_service_->UpdateCustomBackgroundColorAsync( - GURL("different_url"), image, image_fetcher::RequestMetadata()); + base::TimeTicks::Now(), image, image_fetcher::RequestMetadata()); thread_bundle()->RunUntilIdle(); EXPECT_FALSE(CheckBackgroundColor( SK_ColorRED, @@ -607,9 +607,52 @@ // Background color should update. instant_service_->UpdateCustomBackgroundColorAsync( - kUrl, image, image_fetcher::RequestMetadata()); + instant_service_->GetBackgroundUpdatedTimestampForTesting(), image, + image_fetcher::RequestMetadata()); thread_bundle()->RunUntilIdle(); EXPECT_TRUE(CheckBackgroundColor( SK_ColorRED, pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict))); } + +TEST_F(InstantServiceTest, LocalImageDoesNotUpdateCustomBackgroundColor) { + SkBitmap bitmap; + bitmap.allocN32Pixels(32, 32); + bitmap.eraseColor(SK_ColorRED); + gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap); + sync_preferences::TestingPrefServiceSyncable* pref_service = + profile()->GetTestingPrefService(); + + base::FilePath profile_path = profile()->GetPath(); + base::FilePath path(profile_path.AppendASCII("test_file")); + base::FilePath copy_path(profile_path.AppendASCII( + chrome::kChromeSearchLocalNtpBackgroundFilename)); + base::WriteFile(path, "background_image", 16); + + instant_service_->SelectLocalBackgroundImage(path); + + ASSERT_FALSE(instant_service_->IsCustomBackgroundSet()); + + const GURL kUrl("https://www.foo.com"); + const std::string kAttributionLine1 = "foo"; + const std::string kAttributionLine2 = "bar"; + const GURL kActionUrl("https://www.bar.com"); + + SetUserSelectedDefaultSearchProvider("{google:baseURL}"); + instant_service_->AddValidBackdropUrlForTesting(kUrl); + instant_service_->SetCustomBackgroundURLWithAttributions( + kUrl, kAttributionLine1, kAttributionLine2, kActionUrl); + base::TimeTicks time_set = + instant_service_->GetBackgroundUpdatedTimestampForTesting(); + + instant_service_->SelectLocalBackgroundImage(path); + + // Background color will not update if a local image was uploaded in the + // meantime. + instant_service_->UpdateCustomBackgroundColorAsync( + time_set, image, image_fetcher::RequestMetadata()); + thread_bundle()->RunUntilIdle(); + EXPECT_FALSE(CheckBackgroundColor( + SK_ColorRED, + pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict))); +}
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc index a316f52..ba9fb7b5 100644 --- a/chrome/browser/sharing/sharing_device_registration.cc +++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -84,10 +84,9 @@ case instance_id::InstanceID::SUCCESS: gcm_driver_->GetEncryptionInfo( kSharingFCMAppID, - base::AdaptCallbackForRepeating(base::BindOnce( - &SharingDeviceRegistration::OnEncryptionInfoReceived, - weak_ptr_factory_.GetWeakPtr(), std::move(callback), - std::move(public_key), fcm_registration_token))); + base::BindOnce(&SharingDeviceRegistration::OnEncryptionInfoReceived, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + std::move(public_key), fcm_registration_token)); break; case instance_id::InstanceID::NETWORK_ERROR: case instance_id::InstanceID::SERVER_ERROR: @@ -106,13 +105,14 @@ RegistrationCallback callback, std::string public_key, const std::string& fcm_registration_token, - const std::string& p256dh, - const std::string& auth_secret) { + std::string p256dh, + std::string auth_secret) { int device_capabilities = GetDeviceCapabilities(); std::string device_guid = local_device_info_provider_->GetLocalDeviceInfo()->guid(); - SharingSyncPreference::Device device(fcm_registration_token, p256dh, - auth_secret, device_capabilities); + SharingSyncPreference::Device device( + fcm_registration_token, std::move(p256dh), std::move(auth_secret), + device_capabilities); sharing_sync_preference_->SetSyncDevice(device_guid, device); registered_public_key_ = std::move(public_key);
diff --git a/chrome/browser/sharing/sharing_device_registration.h b/chrome/browser/sharing/sharing_device_registration.h index a000c1a..280398a8 100644 --- a/chrome/browser/sharing/sharing_device_registration.h +++ b/chrome/browser/sharing/sharing_device_registration.h
@@ -75,8 +75,8 @@ void OnEncryptionInfoReceived(RegistrationCallback callback, std::string public_key, const std::string& fcm_registration_token, - const std::string& p256dh, - const std::string& auth_secret); + std::string p256dh, + std::string auth_secret); // Computes and returns a bitmask of all capabilities supported by the device. int GetDeviceCapabilities() const;
diff --git a/chrome/browser/sharing/sharing_device_registration_unittest.cc b/chrome/browser/sharing/sharing_device_registration_unittest.cc index 8105dc2..6d02fb29 100644 --- a/chrome/browser/sharing/sharing_device_registration_unittest.cc +++ b/chrome/browser/sharing/sharing_device_registration_unittest.cc
@@ -91,11 +91,11 @@ class FakeEncryptionGCMDriver : public FakeGCMDriver { public: - FakeEncryptionGCMDriver() {} - ~FakeEncryptionGCMDriver() override {} + FakeEncryptionGCMDriver() = default; + ~FakeEncryptionGCMDriver() override = default; void GetEncryptionInfo(const std::string& app_id, - const GetEncryptionInfoCallback& callback) override { + GetEncryptionInfoCallback callback) override { std::move(callback).Run(kDevicep256dh, kDeviceAuthSecret); }
diff --git a/chrome/browser/sharing/sharing_sync_preference.cc b/chrome/browser/sharing/sharing_sync_preference.cc index 5a3c5a65..e678c1d 100644 --- a/chrome/browser/sharing/sharing_sync_preference.cc +++ b/chrome/browser/sharing/sharing_sync_preference.cc
@@ -71,13 +71,13 @@ } // namespace -SharingSyncPreference::Device::Device(const std::string& fcm_token, - const std::string& p256dh, - const std::string& auth_secret, +SharingSyncPreference::Device::Device(std::string fcm_token, + std::string p256dh, + std::string auth_secret, const int capabilities) - : fcm_token(fcm_token), - p256dh(p256dh), - auth_secret(auth_secret), + : fcm_token(std::move(fcm_token)), + p256dh(std::move(p256dh)), + auth_secret(std::move(auth_secret)), capabilities(capabilities) {} SharingSyncPreference::Device::Device(Device&& other) = default; @@ -235,5 +235,6 @@ return base::nullopt; } - return Device(*fcm_token, p256dh, auth_secret, *capabilities); + return Device(*fcm_token, std::move(p256dh), std::move(auth_secret), + *capabilities); }
diff --git a/chrome/browser/sharing/sharing_sync_preference.h b/chrome/browser/sharing/sharing_sync_preference.h index e0965ae9..4824b19 100644 --- a/chrome/browser/sharing/sharing_sync_preference.h +++ b/chrome/browser/sharing/sharing_sync_preference.h
@@ -28,9 +28,9 @@ class SharingSyncPreference { public: struct Device { - Device(const std::string& fcm_token, - const std::string& p256dh, - const std::string& auth_secret, + Device(std::string fcm_token, + std::string p256dh, + std::string auth_secret, const int capabilities); Device(Device&& other); ~Device();
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc index 19b7532..427cf15 100644 --- a/chrome/browser/signin/chrome_signin_helper.cc +++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -59,6 +59,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/ui/settings_window_manager_chromeos.h" +#include "chrome/browser/ui/webui/signin/inline_login_handler_dialog_chromeos.h" #include "chromeos/constants/chromeos_switches.h" #endif @@ -210,14 +211,26 @@ // // - Going Incognito (already handled in above switch-case). // - Displaying the Account Manager for managing accounts. + // - Displaying a reauthentication window: Enterprise GSuite Accounts could + // have been forced through an online in-browser sign-in for sensitive + // webpages, thereby decreasing their session validity. After their session + // expires, they will receive a "Mirror" re-authentication request for all + // Google web properties. // Do not display Account Manager if the navigation happened in the // "background". if (!chrome::FindBrowserWithWebContents(web_contents)) return; - chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( - profile, chrome::kAccountManagerSubPage); + if (manage_accounts_params.email.empty()) { + // Display Account Manager for managing accounts. + chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( + profile, chrome::kAccountManagerSubPage); + } else { + // Display a re-authentication dialog. + chromeos::InlineLoginHandlerDialogChromeOS::Show( + manage_accounts_params.email); + } return; }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 98c5e5c..e708e6a 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2738,6 +2738,8 @@ "views/global_media_controls/media_dialog_view.h", "views/global_media_controls/media_notification_container_impl.cc", "views/global_media_controls/media_notification_container_impl.h", + "views/global_media_controls/media_notification_list_view.cc", + "views/global_media_controls/media_notification_list_view.h", "views/global_media_controls/media_toolbar_button_view.cc", "views/global_media_controls/media_toolbar_button_view.h", "views/hover_button.cc",
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc index 6850d89..69daceb 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -82,7 +82,7 @@ AutofillPopupControllerImpl::~AutofillPopupControllerImpl() {} void AutofillPopupControllerImpl::Show( - const std::vector<autofill::Suggestion>& suggestions, + const std::vector<Suggestion>& suggestions, bool autoselect_first_suggestion, PopupType popup_type) { SetValues(suggestions); @@ -121,7 +121,8 @@ #if defined(OS_ANDROID) if (popup_type == PopupType::kPasswords && TouchToFillController::AllowedForWebContents(web_contents_)) { - TouchToFillController::GetOrCreate(web_contents_)->Show(suggestions); + TouchToFillController::GetOrCreate(web_contents_) + ->Show(suggestions, GetWeakPtr()); } ManualFillingController::GetOrCreate(web_contents_) @@ -185,7 +186,7 @@ // Add a separator if there are any other values. if (!suggestions_.empty() && suggestions_[0].frontend_id != POPUP_ITEM_ID_SEPARATOR) { - suggestions_.insert(suggestions_.begin(), autofill::Suggestion()); + suggestions_.insert(suggestions_.begin(), Suggestion()); suggestions_[0].frontend_id = POPUP_ITEM_ID_SEPARATOR; elided_values_.insert(elided_values_.begin(), base::string16()); elided_labels_.insert(elided_labels_.begin(), base::string16()); @@ -313,7 +314,7 @@ } void AutofillPopupControllerImpl::AcceptSuggestion(int index) { - const autofill::Suggestion& suggestion = suggestions_[index]; + const Suggestion& suggestion = suggestions_[index]; #if defined(OS_ANDROID) auto mf_controller = ManualFillingController::GetOrCreate(web_contents_); // Accepting a suggestion should hide all suggestions. To prevent them from @@ -349,8 +350,7 @@ return controller_common_.text_direction == base::i18n::RIGHT_TO_LEFT; } -const std::vector<autofill::Suggestion> -AutofillPopupControllerImpl::GetSuggestions() { +const std::vector<Suggestion> AutofillPopupControllerImpl::GetSuggestions() { return suggestions_; } @@ -376,8 +376,7 @@ return suggestions_.size(); } -const autofill::Suggestion& AutofillPopupControllerImpl::GetSuggestionAt( - int row) const { +const Suggestion& AutofillPopupControllerImpl::GetSuggestionAt(int row) const { return suggestions_[row]; } @@ -511,7 +510,7 @@ } void AutofillPopupControllerImpl::SetValues( - const std::vector<autofill::Suggestion>& suggestions) { + const std::vector<Suggestion>& suggestions) { suggestions_ = suggestions; elided_values_.resize(suggestions.size()); elided_labels_.resize(suggestions.size());
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h index e5db4f0..c57e3e64 100644 --- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h +++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -7,6 +7,8 @@ #include <stddef.h> +#include <vector> + #include "base/gtest_prod_util.h" #include "base/i18n/rtl.h" #include "base/memory/weak_ptr.h" @@ -49,7 +51,7 @@ base::i18n::TextDirection text_direction); // Shows the popup, or updates the existing popup with the given values. - virtual void Show(const std::vector<autofill::Suggestion>& suggestions, + virtual void Show(const std::vector<Suggestion>& suggestions, bool autoselect_first_suggestion, PopupType popup_type); @@ -90,7 +92,7 @@ const gfx::RectF& element_bounds() const override; void SetElementBounds(const gfx::RectF& bounds); bool IsRTL() const override; - const std::vector<autofill::Suggestion> GetSuggestions() override; + const std::vector<Suggestion> GetSuggestions() override; #if !defined(OS_ANDROID) void SetTypesetter(gfx::Typesetter typesetter) override; int GetElidedValueWidthForRow(int row) override; @@ -101,7 +103,7 @@ void OnSuggestionsChanged() override; void AcceptSuggestion(int index) override; int GetLineCount() const override; - const autofill::Suggestion& GetSuggestionAt(int row) const override; + const Suggestion& GetSuggestionAt(int row) const override; const base::string16& GetElidedValueAt(int row) const override; const base::string16& GetElidedLabelAt(int row) const override; bool GetRemovalConfirmationText(int list_index, @@ -129,7 +131,7 @@ // Set the Autofill entry values. Exposed to allow tests to set these values // without showing the popup. - void SetValues(const std::vector<autofill::Suggestion>& suggestions); + void SetValues(const std::vector<Suggestion>& suggestions); AutofillPopupView* view() { return view_; } @@ -166,7 +168,7 @@ base::i18n::TextDirection text_direction_; // The current Autofill query values. - std::vector<autofill::Suggestion> suggestions_; + std::vector<Suggestion> suggestions_; // Elided values and labels corresponding to the suggestions_ vector to // ensure that it fits on the screen.
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc index ad0a04cd..e216d575 100644 --- a/chrome/browser/ui/global_media_controls/media_dialog_controller.cc +++ b/chrome/browser/ui/global_media_controls/media_dialog_controller.cc
@@ -88,16 +88,6 @@ void MediaDialogController::OnReceivedAudioFocusRequests( std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) { - // TODO(steimel): Show all controllable sessions. - media_session::mojom::AudioFocusRequestStatePtr active_session; - for (auto& session : base::Reversed(sessions)) { - if (!session->session_info->is_controllable) - continue; - - active_session = session.Clone(); - break; - } - - if (active_session) - OnFocusGained(std::move(active_session)); + for (auto& session : sessions) + OnFocusGained(std::move(session)); }
diff --git a/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc index d2c366b..e9e6fa9 100644 --- a/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc +++ b/chrome/browser/ui/global_media_controls/media_dialog_controller_unittest.cc
@@ -48,15 +48,20 @@ } protected: - void SimulateFocusGained(const base::UnguessableToken& id, - bool controllable) { + AudioFocusRequestStatePtr CreateFocusRequest(const base::UnguessableToken& id, + bool controllable) { MediaSessionInfoPtr session_info(MediaSessionInfo::New()); session_info->is_controllable = controllable; AudioFocusRequestStatePtr focus(AudioFocusRequestState::New()); focus->request_id = id; focus->session_info = std::move(session_info); - controller_->OnFocusGained(std::move(focus)); + return focus; + } + + void SimulateFocusGained(const base::UnguessableToken& id, + bool controllable) { + controller_->OnFocusGained(CreateFocusRequest(id, controllable)); } void SimulateFocusLost(const base::UnguessableToken& id) { @@ -81,6 +86,11 @@ item_itr->second.MediaSessionMetadataChanged(std::move(metadata)); } + void SimulateReceivedAudioFocusRequests( + std::vector<AudioFocusRequestStatePtr> requests) { + controller_->OnReceivedAudioFocusRequests(std::move(requests)); + } + MockMediaDialogControllerDelegate& delegate() { return delegate_; } private: @@ -113,3 +123,25 @@ SimulateFocusGained(id, false); SimulateNecessaryMetadata(id); } + +TEST_F(MediaDialogControllerTest, ShowsAllInitialControllableSessions) { + base::UnguessableToken controllable1_id = base::UnguessableToken::Create(); + base::UnguessableToken uncontrollable_id = base::UnguessableToken::Create(); + base::UnguessableToken controllable2_id = base::UnguessableToken::Create(); + + EXPECT_CALL(delegate(), ShowMediaSession(controllable1_id.ToString(), _)); + EXPECT_CALL(delegate(), ShowMediaSession(uncontrollable_id.ToString(), _)) + .Times(0); + EXPECT_CALL(delegate(), ShowMediaSession(controllable2_id.ToString(), _)); + + std::vector<AudioFocusRequestStatePtr> requests; + requests.push_back(CreateFocusRequest(controllable1_id, true)); + requests.push_back(CreateFocusRequest(uncontrollable_id, false)); + requests.push_back(CreateFocusRequest(controllable2_id, true)); + + SimulateReceivedAudioFocusRequests(std::move(requests)); + + SimulateNecessaryMetadata(controllable1_id); + SimulateNecessaryMetadata(uncontrollable_id); + SimulateNecessaryMetadata(controllable2_id); +}
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc index 2a8274ea..04586a9 100644 --- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc +++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h" +#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h" #include "services/media_session/public/mojom/media_session.mojom.h" #include "ui/views/background.h" #include "ui/views/controls/label.h" @@ -48,27 +49,13 @@ void MediaDialogView::ShowMediaSession( const std::string& id, base::WeakPtr<media_message_center::MediaNotificationItem> item) { - // Do nothing if we're already showing this media session. - if (active_session_id_.has_value() && *active_session_id_ == id) - return; - - active_session_id_ = id; - - auto active_session_container = - std::make_unique<MediaNotificationContainerImpl>(this, item); - active_session_container_ = AddChildView(std::move(active_session_container)); + active_sessions_view_->ShowNotification( + id, std::make_unique<MediaNotificationContainerImpl>(this, item)); OnAnchorBoundsChanged(); } void MediaDialogView::HideMediaSession(const std::string& id) { - // Do nothing if we're not showing this media session. - if (!active_session_id_.has_value() || active_session_id_ != id) - return; - - active_session_id_ = base::nullopt; - - RemoveChildView(active_session_container_); - active_session_container_ = nullptr; + active_sessions_view_->HideNotification(id); OnAnchorBoundsChanged(); } @@ -86,7 +73,7 @@ gfx::Size MediaDialogView::CalculatePreferredSize() const { // If we have an active session, then fit to it. - if (active_session_container_) + if (!active_sessions_view_->empty()) return views::BubbleDialogDelegateView::CalculatePreferredSize(); // Otherwise, use a standard size for bubble dialogs. @@ -98,7 +85,9 @@ MediaDialogView::MediaDialogView(views::View* anchor_view, service_manager::Connector* connector) : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), - controller_(connector, this) {} + controller_(connector, this), + active_sessions_view_( + AddChildView(std::make_unique<MediaNotificationListView>())) {} MediaDialogView::~MediaDialogView() = default;
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h index ae83d6d..f305616 100644 --- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.h +++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.h
@@ -14,7 +14,7 @@ class Connector; } // namespace service_manager -class MediaNotificationContainerImpl; +class MediaNotificationListView; // Dialog that shows media controls that control the active media session. class MediaDialogView : public views::BubbleDialogDelegateView, @@ -54,11 +54,7 @@ MediaDialogController controller_; - // TODO(steimel): We should support showing multiple sessions instead of just - // the active one. - MediaNotificationContainerImpl* active_session_container_ = nullptr; - - base::Optional<std::string> active_session_id_; + MediaNotificationListView* const active_sessions_view_; DISALLOW_COPY_AND_ASSIGN(MediaDialogView); };
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc new file mode 100644 index 0000000..0ff6fa4 --- /dev/null +++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc
@@ -0,0 +1,34 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h" + +#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h" +#include "ui/views/layout/box_layout.h" + +MediaNotificationListView::MediaNotificationListView() { + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kVertical)); +} + +MediaNotificationListView::~MediaNotificationListView() = default; + +void MediaNotificationListView::ShowNotification( + const std::string& id, + std::unique_ptr<MediaNotificationContainerImpl> notification) { + DCHECK(!base::Contains(notifications_, id)); + DCHECK_NE(nullptr, notification.get()); + + notifications_[id] = AddChildView(std::move(notification)); + PreferredSizeChanged(); +} + +void MediaNotificationListView::HideNotification(const std::string& id) { + if (!base::Contains(notifications_, id)) + return; + + RemoveChildView(notifications_[id]); + notifications_.erase(id); + PreferredSizeChanged(); +}
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h new file mode 100644 index 0000000..302deec8 --- /dev/null +++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.h
@@ -0,0 +1,34 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_ + +#include <map> +#include <memory> + +#include "ui/views/view.h" + +class MediaNotificationContainerImpl; + +// MediaNotificationListView is a container that holds a list of active media +// sessions. +class MediaNotificationListView : public views::View { + public: + MediaNotificationListView(); + ~MediaNotificationListView() override; + + void ShowNotification( + const std::string& id, + std::unique_ptr<MediaNotificationContainerImpl> notification); + void HideNotification(const std::string& id); + bool empty() { return notifications_.empty(); } + + private: + std::map<const std::string, MediaNotificationContainerImpl*> notifications_; + + DISALLOW_COPY_AND_ASSIGN(MediaNotificationListView); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc index b644f2f..80265fea 100644 --- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc +++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -16,7 +16,7 @@ #include "chrome/browser/ui/views/payments/payment_request_views_util.h" #include "chrome/grit/generated_resources.h" #include "components/payments/content/icon/icon_size.h" -#include "components/payments/core/error_strings.h" +#include "components/payments/core/native_error_strings.h" #include "components/payments/core/url_util.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc index 8172347..8484f57 100644 --- a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.cc
@@ -7,10 +7,12 @@ #include <utility> #include "base/bind.h" +#include "base/strings/string16.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/l10n/time_format.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/webui/web_ui_util.h" #include "ui/chromeos/resources/grit/ui_chromeos_resources.h" @@ -74,10 +76,19 @@ for (int n = 0; n < response.accounts_size(); ++n) { const kerberos::Account& account = response.accounts(n); + // Format validity time as 'xx hours yy minutes' for validity < 1 day and + // 'nn days' otherwise. + base::TimeDelta tgt_validity = + base::TimeDelta::FromSeconds(account.tgt_validity_seconds()); + const base::string16 valid_for_duration = ui::TimeFormat::Detailed( + ui::TimeFormat::FORMAT_DURATION, ui::TimeFormat::LENGTH_LONG, + tgt_validity < base::TimeDelta::FromDays(1) ? -1 : 0, tgt_validity); + base::DictionaryValue account_dict; account_dict.SetString("principalName", account.principal_name()); account_dict.SetString("config", account.krb5conf()); account_dict.SetBoolean("isSignedIn", account.tgt_validity_seconds() > 0); + account_dict.SetString("validForDuration", valid_for_duration); account_dict.SetBoolean("isActive", account.principal_name() == active_principal); account_dict.SetBoolean("isManaged", account.is_managed()); @@ -107,8 +118,8 @@ const bool allow_existing = args->GetList()[5].GetBool(); KerberosCredentialsManager::Get().AddAccountAndAuthenticate( - std::move(principal_name), false /* is_managed */, password, - remember_password, config, allow_existing, + principal_name, false /* is_managed */, password, remember_password, + config, allow_existing, base::BindOnce(&KerberosAccountsHandler::OnAddAccountAndAuthenticate, weak_factory_.GetWeakPtr(), callback_id)); } @@ -124,15 +135,19 @@ const base::ListValue* args) { AllowJavascript(); - CHECK_EQ(1U, args->GetSize()); - const std::string& principal_name = args->GetList()[0].GetString(); + CHECK_EQ(2U, args->GetSize()); + const std::string& callback_id = args->GetList()[0].GetString(); + const std::string& principal_name = args->GetList()[1].GetString(); - // Note that we're observing the credentials manager, so OnAccountsChanged() - // is called when an account is removed, which calls RefreshUI(). Thus, it's - // fine to pass an EmptyResultCallback() in here and not something that calls - // RefreshUI(). KerberosCredentialsManager::Get().RemoveAccount( - principal_name, KerberosCredentialsManager::EmptyResultCallback()); + principal_name, base::BindOnce(&KerberosAccountsHandler::OnRemoveAccount, + weak_factory_.GetWeakPtr(), callback_id)); +} + +void KerberosAccountsHandler::OnRemoveAccount(const std::string& callback_id, + kerberos::ErrorType error) { + ResolveJavascriptCallback(base::Value(callback_id), + base::Value(static_cast<int>(error))); } void KerberosAccountsHandler::HandleSetAsActiveKerberosAccount(
diff --git a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h index 4bdb8cd..53d0a64 100644 --- a/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h +++ b/chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h
@@ -51,6 +51,10 @@ // WebUI "removeKerberosAccount" message callback. void HandleRemoveKerberosAccount(const base::ListValue* args); + // Callback for the credential manager's RemoveAccount method. + void OnRemoveAccount(const std::string& callback_id, + kerberos::ErrorType error); + // WebUI "setAsActiveKerberosAccount" message callback. void HandleSetAsActiveKerberosAccount(const base::ListValue* args);
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index 4677de7..64abc6b 100644 --- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1806,7 +1806,6 @@ {"kerberosAccountsSubMenuLabel", IDS_SETTINGS_KERBEROS_ACCOUNTS_SUBMENU_LABEL}, {"kerberosAccountsPageTitle", IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE}, - {"kerberosAccountsDescription", IDS_SETTINGS_KERBEROS_ACCOUNTS_DESCRIPTION}, {"kerberosAccountsListHeader", IDS_SETTINGS_KERBEROS_ACCOUNTS_LIST_HEADER}, {"kerberosAccountsAddAccountLabel", IDS_SETTINGS_KERBEROS_ACCOUNTS_ADD_ACCOUNT_LABEL}, @@ -1816,17 +1815,25 @@ IDS_SETTINGS_KERBEROS_ACCOUNTS_SET_AS_ACTIVE_ACCOUNT_LABEL}, {"kerberosAccountsRemoveAccountLabel", IDS_SETTINGS_KERBEROS_ACCOUNTS_REMOVE_ACCOUNT_LABEL}, + {"kerberosAccountsAccountRemovedTip", + IDS_SETTINGS_KERBEROS_ACCOUNTS_ACCOUNT_REMOVED_TIP}, {"kerberosAccountsSignedIn", IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_IN}, {"kerberosAccountsSignedOut", IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_OUT}, {"kerberosAccountsReauthenticationLabel", IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL}, + {"kerberosAccountsTicketActive", + IDS_SETTINGS_KERBEROS_ACCOUNTS_TICKET_ACTIVE}, {"addKerberosAccount", IDS_SETTINGS_ADD_KERBEROS_ACCOUNT}, + {"refreshKerberosAccount", IDS_SETTINGS_REFRESH_KERBEROS_ACCOUNT}, + {"addKerberosAccountDescription", + IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_DESCRIPTION}, {"addKerberosAccountRememberPassword", IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REMEMBER_PASSWORD}, + {"addKerberosAccountRefreshButtonLabel", + IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REFRESH_BUTTON_LABEL}, {"kerberosUsername", IDS_SETTINGS_KERBEROS_USERNAME}, {"kerberosPassword", IDS_SETTINGS_KERBEROS_PASSWORD}, - {"kerberosConfig", IDS_SETTINGS_KERBEROS_CONFIG}, - {"kerberosAdvancedConfigLabel", + {"kerberosAccountsAdvancedConfigLabel", IDS_SETTINGS_KERBEROS_ACCOUNTS_ADVANCED_CONFIG_LABEL}, {"kerberosAdvancedConfigTitle", IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_TITLE}, @@ -2157,6 +2164,13 @@ html_source->AddString( "defaultKerberosConfig", chromeos::KerberosCredentialsManager::GetDefaultKerberosConfig()); + + // Kerberos accounts page with "Learn more" link. + html_source->AddString( + "kerberosAccountsDescription", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_KERBEROS_ACCOUNTS_DESCRIPTION, + GetHelpUrlWithBoard(chrome::kKerberosAccountsLearnMoreURL))); #endif }
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index fa89d6d2..bde87c7 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -210,6 +210,10 @@ // More info about the project may be found here: // https://docs.google.com/document/d/18Ijj8YlC8Q3EWRzLspIi2dGxg4vIBVe5sJgMPt9SWYo const base::Feature kWilcoDtc{"WilcoDtc", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Enable uploading of a zip archive of system logs instead of individual files. +const base::Feature kUploadZippedSystemLogs{"UploadZippedSystemLogs", + base::FEATURE_DISABLED_BY_DEFAULT}; #endif #if defined(OS_CHROMEOS) || defined(OS_LINUX)
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index b8d830e..97f35037 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -123,6 +123,8 @@ COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kUsageTimeLimitPolicy; COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kWilcoDtc; +COMPONENT_EXPORT(CHROME_FEATURES) +extern const base::Feature kUploadZippedSystemLogs; #endif #if defined(OS_CHROMEOS) || defined(OS_LINUX)
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 71e50ac..285e851 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -308,6 +308,9 @@ const char kInstantTetheringLearnMoreURL[] = "https://support.google.com/chromebook?p=instant_tethering"; +const char kKerberosAccountsLearnMoreURL[] = + "https://support.google.com/chromebook/?p=kerberos_accounts"; + const char kMultiDeviceLearnMoreURL[] = "https://support.google.com/chromebook/?p=multi_device";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index 2f82f1b0..0f32538 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h
@@ -261,6 +261,9 @@ // The URL for the "learn more" link for Instant Tethering. extern const char kInstantTetheringLearnMoreURL[]; +// The URL for the "Learn more" link for Kerberos accounts. +extern const char kKerberosAccountsLearnMoreURL[]; + // The URL for the "Learn more" link in the connected devices. extern const char kMultiDeviceLearnMoreURL[];
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index 5a78397..ddf4f1d 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -539,8 +539,9 @@ "if (window.chrome &&" " window.chrome.embeddedSearch &&" " window.chrome.embeddedSearch.newTabPage &&" - " window.chrome.embeddedSearch.newTabPage.onthemechange &&" - " typeof window.chrome.embeddedSearch.newTabPage.onthemechange ==" + " window.chrome.embeddedSearch.newTabPage.onlocalbackgroundselected &&" + " typeof " + "window.chrome.embeddedSearch.newTabPage.onlocalbackgroundselected ==" " 'function') {" " " "window.chrome.embeddedSearch.newTabPage."
diff --git a/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn b/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn index 0c3b4ff..73c36631 100644 --- a/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn +++ b/chrome/services/cups_ipp_parser/public/cpp/BUILD.gn
@@ -27,7 +27,7 @@ # Fuzzer target only available on fuzzing builds. # TODO(crbug.com/831914): Enable this fuzzer once its running on CrOS only. - if (use_fuzzing_engine && is_chromeos) { + if (false) { # use_fuzzing_engine && is_chromeos fuzzer_test("ipp_message_parser_fuzzer") { sources = [ "ipp_message_parser_fuzzer.cc",
diff --git a/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cc b/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cc index d86506f..5ed5489 100644 --- a/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cc +++ b/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cc
@@ -11,6 +11,7 @@ #include "base/strings/strcat.h" #include "base/strings/string_split.h" +#include "base/values.h" #include "net/http/http_util.h" namespace ipp_converter { @@ -21,6 +22,8 @@ const char kStatusDelimiter[] = " "; const char kHeaderDelimiter[] = ": "; +const size_t kIppDateSize = 11; + // Callback used with ippReadIO (libCUPS API), // Repeatedly used to copy IPP request buffer -> ipp_t. ssize_t IppRead(base::span<const uint8_t>* src, @@ -87,6 +90,7 @@ case IPP_TAG_DATE: return ValueType::DATE; case IPP_TAG_INTEGER: + case IPP_TAG_ENUM: return ValueType::INTEGER; // Below string cases take from libCUPS ippAttributeString API @@ -107,9 +111,44 @@ } // Fail to convert any unrecognized types. + DVLOG(1) << "Failed to convert CUPS value tag, type " << value_tag; return base::nullopt; } +std::vector<bool> IppGetBools(ipp_attribute_t* attr) { + std::vector<bool> ret; + for (int i = 0; i < ippGetCount(attr); ++i) { + // No decipherable failure condition for this libCUPS method. + bool v = ippGetBoolean(attr, i); + ret.emplace_back(v); + } + return ret; +} + +base::Optional<std::vector<int>> IppGetInts(ipp_attribute_t* attr) { + std::vector<int> ret; + for (int i = 0; i < ippGetCount(attr); ++i) { + int v = ippGetInteger(attr, i); + if (!v) { + return base::nullopt; + } + ret.emplace_back(v); + } + return ret; +} + +base::Optional<std::vector<std::string>> IppGetStrings(ipp_attribute_t* attr) { + std::vector<std::string> ret; + for (int i = 0; i < ippGetCount(attr); ++i) { + const char* v = ippGetString( + attr, i, nullptr /* TODO(crbug.com/945409): figure out language */); + if (!v) { + return base::nullopt; + } + ret.emplace_back(v); + } + return ret; +} } // namespace base::Optional<std::vector<std::string>> ParseRequestLine( @@ -316,49 +355,41 @@ } attrptr->type = type.value(); - std::vector<cups_ipp_parser::mojom::ValuePtr> values; - for (int i = 0; i < ippGetCount(attr); ++i) { - auto value = cups_ipp_parser::mojom::Value::New(); - switch (attrptr->type) { - case ValueType::BOOLEAN: { - auto v = ippGetBoolean(attr, i); - if (!v) { - return nullptr; - } - value->set_bool_value(v); - break; - } - case ValueType::DATE: { - auto* v = ippGetDate(attr, i); - if (!v) { - return nullptr; - } - value->set_char_value(*v); - break; - } - case ValueType::INTEGER: { - auto v = ippGetInteger(attr, i); - if (!v) { - return nullptr; - } - value->set_int_value(v); - break; - } - case ValueType::STRING: { - auto* v = ippGetString( - attr, i, NULL /* TODO(crbug/781061): figure out language */); - if (!v) { - return nullptr; - } - value->set_string_value(v); - break; - } - default: - NOTREACHED(); + attrptr->value = cups_ipp_parser::mojom::IppAttributeValue::New(); + switch (attrptr->type) { + case ValueType::BOOLEAN: { + attrptr->value->set_bools(IppGetBools(attr)); + break; } - values.emplace_back(std::move(value)); + case ValueType::DATE: { + // Note: We never expect date-attributes to be single-valued. + const uint8_t* v = + reinterpret_cast<const uint8_t*>(ippGetDate(attr, 0)); + if (!v) { + return nullptr; + } + attrptr->value->set_date(std::vector<uint8_t>(v, v + kIppDateSize)); + break; + } + case ValueType::INTEGER: { + auto vals = IppGetInts(attr); + if (!vals.has_value()) { + return nullptr; + } + attrptr->value->set_ints(*vals); + break; + } + case ValueType::STRING: { + auto vals = IppGetStrings(attr); + if (!vals.has_value()) { + return nullptr; + } + attrptr->value->set_strings(*vals); + break; + } + default: + NOTREACHED(); } - attrptr->values = std::move(values); attributes.emplace_back(std::move(attrptr)); }
diff --git a/chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom b/chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom index dd8fbde..584617d 100644 --- a/chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom +++ b/chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom
@@ -16,11 +16,15 @@ STRING, }; -union Value { - bool bool_value; - int64 int_value; - uint8 char_value; - string string_value; +union IppAttributeValue { + array<bool> bools; + + // Per the RFC1903 DateAndTime specification, date's are serialized as a + // string of 11 octets. + array<uint8> date; + + array<int32> ints; + array<string> strings; }; struct IppAttribute { @@ -28,7 +32,7 @@ int64 group_tag; int64 value_tag; ValueType type; - array<Value> values; + IppAttributeValue value; }; struct IppMessage {
diff --git a/chrome/services/cups_proxy/ipp_validator.cc b/chrome/services/cups_proxy/ipp_validator.cc index 5485fd75..48e4eb4 100644 --- a/chrome/services/cups_proxy/ipp_validator.cc +++ b/chrome/services/cups_proxy/ipp_validator.cc
@@ -29,57 +29,59 @@ // TODO(crbug.com/945409): Extending to supporting arbitrary locales. const char kLocaleEnglish[] = "en"; -// Following ParseXxx methods translate mojom objects representing IPP -// attribute values to formats libCUPS APIs accept. +// Following ConvertXxx methods translate IPP attribute values to formats' +// libCUPS APIs accept. // Converting to vector<char> for libCUPS API: // ippAddBooleans(..., int num_values, const char *values) -// TODO(crbug.com/945409): Convert cups_ipp_parser::mojom::Value to using -// mojo_base::Value. -base::Optional<std::vector<char>> ParseBooleans( - const std::vector<cups_ipp_parser::mojom::ValuePtr>& values) { +std::vector<char> ConvertBooleans(std::vector<bool> bools) { std::vector<char> ret; - for (auto& value : values) { - if (!value->is_bool_value()) { - return base::nullopt; - } - - ret.push_back(value->get_bool_value() ? 1 : 0); - } - return ret; -} - -// Converting to vector<int> for libCUPS API: -// ippAddIntegers(..., int num_values, const int *values) -base::Optional<std::vector<int>> ParseIntegers( - const std::vector<cups_ipp_parser::mojom::ValuePtr>& values) { - std::vector<int> ret; - for (auto& value : values) { - if (!value->is_int_value()) { - return base::nullopt; - } - - ret.push_back(value->get_int_value()); + for (bool value : bools) { + ret.push_back(value ? 1 : 0); } return ret; } // Converting to vector<const char*> for libCUPS API: // ippAddStrings(..., int num_values, const char *const *values) -base::Optional<std::vector<const char*>> ParseStrings( - const std::vector<cups_ipp_parser::mojom::ValuePtr>& values) { +// Note: The values in the returned vector refer to |strings|; so |strings| +// must outlive them. +std::vector<const char*> ConvertStrings( + const std::vector<std::string>& strings) { std::vector<const char*> ret; - for (auto& value : values) { - if (!value->is_string_value()) { - return base::nullopt; - } - - // ret's cstrings reference |values| strings, so |values| MUST outlive it. - ret.push_back(value->get_string_value().c_str()); + for (auto& value : strings) { + ret.push_back(value.c_str()); } return ret; } +// Depending on |type|, returns the number of values associated with +// |attr_value|. +size_t GetAttributeValuesSize( + const cups_ipp_parser::mojom::IppAttributePtr& attr) { + const auto& attr_value = attr->value; + switch (attr->type) { + case cups_ipp_parser::mojom::ValueType::DATE: + return 1; + + case cups_ipp_parser::mojom::ValueType::BOOLEAN: + DCHECK(attr_value->is_bools()); + return attr_value->get_bools().size(); + case cups_ipp_parser::mojom::ValueType::INTEGER: + DCHECK(attr_value->is_ints()); + return attr_value->get_ints().size(); + case cups_ipp_parser::mojom::ValueType::STRING: + DCHECK(attr_value->is_strings()); + return attr_value->get_strings().size(); + + default: + break; + } + + DVLOG(1) << "Unknown CupsIppParser ValueType " << attr->type; + return 0; +} + } // namespace // Verifies that |method|, |endpoint|, and |http_version| form a valid HTTP @@ -154,71 +156,71 @@ cups_ipp_parser::mojom::IppAttributePtr attribute = std::move(ipp_message->attributes[i]); - if (ValidateAttribute(ipp_oper_id, attribute->name, attribute->type, - attribute->values.size())) + size_t num_values = GetAttributeValuesSize(attribute); + if (!num_values) { return nullptr; + } + + if (!ValidateAttribute(ipp_oper_id, attribute->name, attribute->type, + num_values)) { + return nullptr; + } switch (attribute->type) { case cups_ipp_parser::mojom::ValueType::BOOLEAN: { - base::Optional<std::vector<char>> values = - ParseBooleans(attribute->values); - if (!values.has_value()) { - return nullptr; - } + DCHECK(attribute->value->is_bools()); + std::vector<char> values = + ConvertBooleans(attribute->value->get_bools()); auto* attr = ippAddBooleans( ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag), - attribute->name.c_str(), values->size(), values->data()); + attribute->name.c_str(), values.size(), values.data()); if (!attr) { return nullptr; } break; } - // TODO(crbug.com/945409): Include for multiple value checking. case cups_ipp_parser::mojom::ValueType::DATE: { - if (!attribute->values.front()->is_char_value()) { - return nullptr; - } + DCHECK(attribute->value->is_date()); + std::vector<uint8_t> date = attribute->value->get_date(); - auto date = attribute->values.front()->get_char_value(); - auto* attr = ippAddDate( - ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag), - attribute->name.c_str(), static_cast<ipp_uchar_t*>(&date)); + // Per RFC2910, ipp_uchar_t is defined as an OCTET, so below + // reinterpret_cast is safe. + auto* attr = + ippAddDate(ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag), + attribute->name.c_str(), + reinterpret_cast<const ipp_uchar_t*>(date.data())); if (!attr) { return nullptr; } break; } case cups_ipp_parser::mojom::ValueType::INTEGER: { - base::Optional<std::vector<int>> values = - ParseIntegers(attribute->values); - if (!values.has_value()) { - return nullptr; - } + DCHECK(attribute->value->is_ints()); + std::vector<int> values = attribute->value->get_ints(); auto* attr = ippAddIntegers( ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag), static_cast<ipp_tag_t>(attribute->value_tag), - attribute->name.c_str(), values->size(), values->data()); + attribute->name.c_str(), values.size(), values.data()); if (!attr) { return nullptr; } break; } case cups_ipp_parser::mojom::ValueType::STRING: { - // Note: cstrings_values references attribute->values, i.e. - // cstrings_values MUST outlive attribute->values. - base::Optional<std::vector<const char*>> cstrings_values = - ParseStrings(attribute->values); - if (!cstrings_values.has_value()) { - return nullptr; - } + DCHECK(attribute->value->is_strings()); + + // Note: cstrings_values references attribute->value's strings, i.e. + // attribute->value MUST outlive cstrings_values. + std::vector<const char*> cstrings_values = + ConvertStrings(attribute->value->get_strings()); auto* attr = ippAddStrings( ipp.get(), static_cast<ipp_tag_t>(attribute->group_tag), static_cast<ipp_tag_t>(attribute->value_tag), - attribute->name.c_str(), cstrings_values->size(), kLocaleEnglish, - cstrings_values->data()); + attribute->name.c_str(), cstrings_values.size(), kLocaleEnglish, + cstrings_values.data()); if (!attr) { return nullptr; }
diff --git a/chrome/services/cups_proxy/ipp_validator_unittest.cc b/chrome/services/cups_proxy/ipp_validator_unittest.cc index 76bd154..2f6b618 100644 --- a/chrome/services/cups_proxy/ipp_validator_unittest.cc +++ b/chrome/services/cups_proxy/ipp_validator_unittest.cc
@@ -22,7 +22,6 @@ using cups_ipp_parser::mojom::IppAttributePtr; using cups_ipp_parser::mojom::IppMessagePtr; using cups_ipp_parser::mojom::IppRequestPtr; -using cups_ipp_parser::mojom::Value; using cups_ipp_parser::mojom::ValueType; using Printer = chromeos::Printer; @@ -84,6 +83,7 @@ ret->name = name; ret->group_tag = group_tag; ret->value_tag = value_tag; + ret->value = cups_ipp_parser::mojom::IppAttributeValue::New(); return ret; } @@ -116,12 +116,12 @@ IppAttributePtr attr_charset = BuildAttributePtr( "attributes-charset", IPP_TAG_OPERATION, IPP_TAG_CHARSET); attr_charset->type = ValueType::STRING; - attr_charset->values.push_back(Value::NewStringValue("utf-8")); + attr_charset->value->set_strings({"utf-8"}); IppAttributePtr attr_natlang = BuildAttributePtr( "attributes-natural-language", IPP_TAG_OPERATION, IPP_TAG_LANGUAGE); attr_natlang->type = ValueType::STRING; - attr_natlang->values.push_back(Value::NewStringValue("en")); + attr_natlang->value->set_strings({"en"}); ipp_message->attributes.push_back(std::move(attr_charset)); ipp_message->attributes.push_back(std::move(attr_natlang)); @@ -177,7 +177,7 @@ EXPECT_TRUE(RunValidateIppRequest(request)); } -// TODO(luum): Test IPP validation. +// TODO(crbug.com/945409): Test IPP validation. } // namespace } // namespace cups_proxy
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 57af7a48..449567d 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -579,6 +579,7 @@ "//content/public/test/android:android_test_message_pump_support_java", "//content/shell/android:content_shell_browsertests_java", "//testing/android/native_test:native_test_java", + "//ui/android:ui_full_java", ] java_files = [ "android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsActivity.java",
diff --git a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsApplication.java b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsApplication.java index 8cde625..4556846 100644 --- a/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsApplication.java +++ b/chrome/test/android/browsertests_apk/src/org/chromium/android_browsertests_apk/ChromeBrowserTestsApplication.java
@@ -9,6 +9,7 @@ import org.chromium.base.PathUtils; import org.chromium.chrome.browser.metrics.UmaUtils; import org.chromium.native_test.NativeBrowserTestApplication; +import org.chromium.ui.base.ResourceBundle; /** * A basic chrome.browser.tests {@link android.app.Application}. @@ -27,6 +28,9 @@ if (isBrowserProcess) { // Test-only stuff, see also NativeUnitTest.java. PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); + // ResourceBundle asserts that locale paks have been given to it. + // In test targets there is no list of paks generated. + ResourceBundle.setNoAvailableLocalePaks(); } } }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ViewUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ViewUtils.java index ba58c14b..5db69386 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ViewUtils.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ViewUtils.java
@@ -67,18 +67,21 @@ private boolean hasViewExpectedState(View view) { if (view == null) { - updateFailureReason("No matching view was found!"); + updateFailureReason("No view found to match: " + mViewMatcher.toString()); return (mViewState & VIEW_NULL) != 0; } switch (view.getVisibility()) { case View.VISIBLE: - updateFailureReason("Found view is unexpectedly visible!"); + updateFailureReason("View matching '" + mViewMatcher.toString() + + "' is unexpectedly visible!"); return (mViewState & VIEW_VISIBLE) != 0; case View.INVISIBLE: - updateFailureReason("Found view is unexpectedly invisible!"); + updateFailureReason("View matching '" + mViewMatcher.toString() + + "' is unexpectedly invisible!"); return (mViewState & VIEW_INVISIBLE) != 0; case View.GONE: - updateFailureReason("Found view is unexpectedly gone!"); + updateFailureReason("View matching '" + mViewMatcher.toString() + + "' is unexpectedly gone!"); return (mViewState & VIEW_GONE) != 0; } assert false; // Not Reached.
diff --git a/chrome/test/data/autofill/autofill_test_form.html b/chrome/test/data/autofill/autofill_test_form.html index 4db584c..d6ea3cd 100644 --- a/chrome/test/data/autofill/autofill_test_form.html +++ b/chrome/test/data/autofill/autofill_test_form.html
@@ -1,6 +1,8 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <!-- Autofill generic test form. --> <html> +<!-- The following call prevents the zoom-on-focus animation which caused test flakes. --> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <head> <title>Autofill Test Form</title> </head>
diff --git a/chrome/test/data/password/password_fetch_submit.html b/chrome/test/data/password/password_fetch_submit.html index 05e8065d..39a9871 100644 --- a/chrome/test/data/password/password_fetch_submit.html +++ b/chrome/test/data/password/password_fetch_submit.html
@@ -14,8 +14,9 @@ function send_fetch() { fetch("done.html") + .then(res => res.text()) .then( - function(response) { + function() { if (navigate) { window.top.location.href = "done.html"; } else {
diff --git a/chrome/test/data/webui/extensions/activity_log_history_test.js b/chrome/test/data/webui/extensions/activity_log_history_test.js index 48ceb70..60f6c0c1 100644 --- a/chrome/test/data/webui/extensions/activity_log_history_test.js +++ b/chrome/test/data/webui/extensions/activity_log_history_test.js
@@ -164,163 +164,157 @@ }); } - test('activities are present for extension', function() { + test('activities are present for extension', async function() { proxyDelegate.testActivities = testActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); + Polymer.dom.flush(); - testVisible('#no-activities', false); - testVisible('#loading-activities', false); - testVisible('#activity-list', true); - testVisible('.activity-table-headings', true); + testVisible('#no-activities', false); + testVisible('#loading-activities', false); + testVisible('#activity-list', true); + testVisible('.activity-table-headings', true); - const activityLogItems = getHistoryItems(); - expectEquals(activityLogItems.length, 3); + const activityLogItems = getHistoryItems(); + expectEquals(activityLogItems.length, 3); - // Test the order of the activity log items here. This test is in this - // file because the logic to group activity log items by their API call - // is in activity_log_history.js. - expectEquals( - activityLogItems[0].$$('#activity-key').innerText, - 'i18n.getUILanguage'); - expectEquals(activityLogItems[0].$$('#activity-count').innerText, '40'); + // Test the order of the activity log items here. This test is in this + // file because the logic to group activity log items by their API call + // is in activity_log_history.js. + expectEquals( + activityLogItems[0].$$('#activity-key').innerText, + 'i18n.getUILanguage'); + expectEquals(activityLogItems[0].$$('#activity-count').innerText, '40'); - expectEquals( - activityLogItems[1].$$('#activity-key').innerText, 'Storage.getItem'); - expectEquals(activityLogItems[1].$$('#activity-count').innerText, '35'); + expectEquals( + activityLogItems[1].$$('#activity-key').innerText, 'Storage.getItem'); + expectEquals(activityLogItems[1].$$('#activity-count').innerText, '35'); - expectEquals( - activityLogItems[2].$$('#activity-key').innerText, 'Storage.setItem'); - expectEquals(activityLogItems[2].$$('#activity-count').innerText, '10'); - }); + expectEquals( + activityLogItems[2].$$('#activity-key').innerText, 'Storage.setItem'); + expectEquals(activityLogItems[2].$$('#activity-count').innerText, '10'); }); - test('activities shown match search query', function() { + test('activities shown match search query', async function() { proxyDelegate.testActivities = testActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - const search = activityLogHistory.$$('cr-search-field'); - assertTrue(!!search); + const search = activityLogHistory.$$('cr-search-field'); + assertTrue(!!search); - // Partial, case insensitive search for i18n.getUILanguage. Whitespace is - // also appended to the search term to test trimming. - search.setValue('getuilanguage '); + // Partial, case insensitive search for i18n.getUILanguage. Whitespace is + // also appended to the search term to test trimming. + search.setValue('getuilanguage '); - return proxyDelegate.whenCalled('getFilteredExtensionActivityLog') - .then(() => { - Polymer.dom.flush(); + await proxyDelegate.whenCalled('getFilteredExtensionActivityLog'); - const activityLogItems = getHistoryItems(); - // Since we searched for an API call, we expect only one match as - // activity log entries are grouped by their API call. - expectEquals(activityLogItems.length, 1); - expectEquals( - activityLogItems[0].$$('#activity-key').innerText, - 'i18n.getUILanguage'); + Polymer.dom.flush(); + const activityLogItems = getHistoryItems(); + // Since we searched for an API call, we expect only one match as + // activity log entries are grouped by their API call. + expectEquals(activityLogItems.length, 1); + expectEquals( + activityLogItems[0].$$('#activity-key').innerText, + 'i18n.getUILanguage'); - // Change search query so no results match. - proxyDelegate.resetResolver('getFilteredExtensionActivityLog'); - search.setValue('query that does not match any activities'); + // Change search query so no results match. + proxyDelegate.resetResolver('getFilteredExtensionActivityLog'); + search.setValue('query that does not match any activities'); - return proxyDelegate.whenCalled('getFilteredExtensionActivityLog'); - }) - .then(() => { - Polymer.dom.flush(); + await proxyDelegate.whenCalled('getFilteredExtensionActivityLog'); - testVisible('#no-activities', true); - testVisible('#loading-activities', false); - testVisible('#activity-list', false); - expectEquals(0, getHistoryItems().length); + Polymer.dom.flush(); - proxyDelegate.resetResolver('getExtensionActivityLog'); + testVisible('#no-activities', true); + testVisible('#loading-activities', false); + testVisible('#activity-list', false); + expectEquals(0, getHistoryItems().length); - // Finally, we clear the search query via the #clearSearch button. - // We should see all the activities displayed. - search.$$('#clearSearch').click(); - return proxyDelegate.whenCalled('getExtensionActivityLog'); - }) - .then(() => { - Polymer.dom.flush(); - expectEquals(3, getHistoryItems().length); - }); - }); + proxyDelegate.resetResolver('getExtensionActivityLog'); + + // Finally, we clear the search query via the #clearSearch button. + // We should see all the activities displayed. + search.$$('#clearSearch').click(); + + await proxyDelegate.whenCalled('getExtensionActivityLog'); + + Polymer.dom.flush(); + expectEquals(3, getHistoryItems().length); }); - test('script names shown for content script activities', function() { + test('script names shown for content script activities', async function() { proxyDelegate.testActivities = testContentScriptActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); - const activityLogItems = getHistoryItems(); + Polymer.dom.flush(); + const activityLogItems = getHistoryItems(); - // One activity should be shown for each content script name. - expectEquals(activityLogItems.length, 2); + // One activity should be shown for each content script name. + expectEquals(activityLogItems.length, 2); - expectEquals( - activityLogItems[0].$$('#activity-key').innerText, 'script1.js'); - expectEquals( - activityLogItems[1].$$('#activity-key').innerText, 'script2.js'); - }); + expectEquals( + activityLogItems[0].$$('#activity-key').innerText, 'script1.js'); + expectEquals( + activityLogItems[1].$$('#activity-key').innerText, 'script2.js'); }); - test('other.webRequest fields shown for web request activities', function() { - proxyDelegate.testActivities = testWebRequestActivities; + test( + 'other.webRequest fields shown for web request activities', + async function() { + proxyDelegate.testActivities = testWebRequestActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); - const activityLogItems = getHistoryItems(); + Polymer.dom.flush(); + const activityLogItems = getHistoryItems(); - // First activity should be split into two groups as it has two actions - // recorded in the other.webRequest object. We display the names of these - // actions along with the API call. Second activity should fall back - // to using just the API call as the key. Hence we end up with three - // activity log items. - const expectedItemKeys = [ - 'webRequest.onBeforeSendHeaders (added_request_headers)', - 'webRequest.onBeforeSendHeaders (modified_request_headers)', - 'webRequest.noWebRequestObject' - ]; - const expectedNumItems = expectedItemKeys.length; + // First activity should be split into two groups as it has two actions + // recorded in the other.webRequest object. We display the names of + // these actions along with the API call. Second activity should fall + // back to using just the API call as the key. Hence we end up with + // three activity log items. + const expectedItemKeys = [ + 'webRequest.onBeforeSendHeaders (added_request_headers)', + 'webRequest.onBeforeSendHeaders (modified_request_headers)', + 'webRequest.noWebRequestObject' + ]; + const expectedNumItems = expectedItemKeys.length; - expectEquals(activityLogItems.length, expectedNumItems); + expectEquals(activityLogItems.length, expectedNumItems); - for (let i = 0; i < expectedNumItems; ++i) { - expectEquals( - activityLogItems[i].$$('#activity-key').innerText, - expectedItemKeys[i]); - } - }); - }); + for (let i = 0; i < expectedNumItems; ++i) { + expectEquals( + activityLogItems[i].$$('#activity-key').innerText, + expectedItemKeys[i]); + } + }); - test('expand/collapse all', function() { + test('expand/collapse all', async function() { proxyDelegate.testActivities = testActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); + Polymer.dom.flush(); - const expandableItems = - Array.from(getHistoryItems()) - .filter(item => item.$$('cr-expand-button:not([hidden])')); - expectEquals(2, expandableItems.length); + const expandableItems = + Array.from(getHistoryItems()) + .filter(item => item.$$('cr-expand-button:not([hidden])')); + expectEquals(2, expandableItems.length); - // All items should be collapsed by default. - expectEquals(0, getExpandedItems().length); + // All items should be collapsed by default. + expectEquals(0, getExpandedItems().length); - // Click the dropdown toggle, then expand all. - activityLogHistory.$$('#more-actions').click(); - activityLogHistory.$$('#expand-all-button').click(); + // Click the dropdown toggle, then expand all. + activityLogHistory.$$('#more-actions').click(); + activityLogHistory.$$('#expand-all-button').click(); - Polymer.dom.flush(); - expectEquals(2, getExpandedItems().length); + Polymer.dom.flush(); + expectEquals(2, getExpandedItems().length); - // Collapse all items. - activityLogHistory.$$('#more-actions').click(); - activityLogHistory.$$('#collapse-all-button').click(); + // Collapse all items. + activityLogHistory.$$('#more-actions').click(); + activityLogHistory.$$('#collapse-all-button').click(); - Polymer.dom.flush(); - expectEquals(0, getExpandedItems().length); - }); + Polymer.dom.flush(); + expectEquals(0, getExpandedItems().length); }); test('export activities', async function() { @@ -351,73 +345,72 @@ test( 'clicking on the delete button for an activity row deletes that row', - function() { + async function() { proxyDelegate.testActivities = testActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); - const activityLogItems = getHistoryItems(); + Polymer.dom.flush(); + const activityLogItems = getHistoryItems(); - expectEquals(activityLogItems.length, 3); - proxyDelegate.resetResolver('getExtensionActivityLog'); - activityLogItems[0].$$('#activity-delete').click(); + expectEquals(activityLogItems.length, 3); + proxyDelegate.resetResolver('getExtensionActivityLog'); + activityLogItems[0].$$('#activity-delete').click(); - // We delete the first item so we should only have one item left. This - // chaining reflects the API calls made from activity_log.js. - return proxyDelegate.whenCalled('deleteActivitiesById') - .then(() => proxyDelegate.whenCalled('getExtensionActivityLog')) - .then(() => { - Polymer.dom.flush(); - expectEquals(2, getHistoryItems().length); - }); - }); + // We delete the first item so we should only have one item left. This + // chaining reflects the API calls made from activity_log.js. + await proxyDelegate.whenCalled('deleteActivitiesById'); + await proxyDelegate.whenCalled('getExtensionActivityLog'); + + Polymer.dom.flush(); + expectEquals(2, getHistoryItems().length); }); - test('message shown when no activities present for extension', function() { + test( + 'message shown when no activities present for extension', + async function() { + // Spoof an API call and pretend that the extension has no activities. + proxyDelegate.testActivities = {activities: []}; + await setupActivityLogHistory(); + + Polymer.dom.flush(); + + testVisible('#no-activities', true); + testVisible('#loading-activities', false); + testVisible('#activity-list', false); + expectEquals(0, getHistoryItems().length); + }); + + test('message shown when activities are being fetched', async function() { // Spoof an API call and pretend that the extension has no activities. proxyDelegate.testActivities = {activities: []}; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); + // Pretend the activity log is still loading. + activityLogHistory.pageState_ = ActivityLogPageState.LOADING; - testVisible('#no-activities', true); - testVisible('#loading-activities', false); - testVisible('#activity-list', false); - expectEquals(0, getHistoryItems().length); - }); + Polymer.dom.flush(); + + testVisible('#no-activities', false); + testVisible('#loading-activities', true); + testVisible('#activity-list', false); }); - test('message shown when activities are being fetched', function() { - // Spoof an API call and pretend that the extension has no activities. - proxyDelegate.testActivities = {activities: []}; + test( + 'clicking on clear button clears the activity log history', + async function() { + proxyDelegate.testActivities = testActivities; + await setupActivityLogHistory(); - return setupActivityLogHistory().then(() => { - // Pretend the activity log is still loading. - activityLogHistory.pageState_ = ActivityLogPageState.LOADING; + Polymer.dom.flush(); - Polymer.dom.flush(); + expectEquals(3, getHistoryItems().length); + activityLogHistory.$$('.clear-activities-button').click(); - testVisible('#no-activities', false); - testVisible('#loading-activities', true); - testVisible('#activity-list', false); - }); - }); + await proxyDelegate.whenCalled('deleteActivitiesFromExtension'); - test('clicking on clear button clears the activity log history', function() { - proxyDelegate.testActivities = testActivities; - - return setupActivityLogHistory().then(() => { - Polymer.dom.flush(); - - expectEquals(3, getHistoryItems().length); - activityLogHistory.$$('.clear-activities-button').click(); - return proxyDelegate.whenCalled('deleteActivitiesFromExtension') - .then(() => { - Polymer.dom.flush(); - testVisible('#no-activities', true); - testVisible('.activity-table-headings', false); - expectEquals(0, getHistoryItems().length); - }); - }); - }); + Polymer.dom.flush(); + testVisible('#no-activities', true); + testVisible('.activity-table-headings', false); + expectEquals(0, getHistoryItems().length); + }); });
diff --git a/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js b/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js index 02a3605..f21a91d 100644 --- a/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js +++ b/chrome/test/data/webui/settings/people_page_kerberos_accounts_test.js
@@ -15,6 +15,7 @@ isManaged: false, passwordWasRemembered: false, pic: 'pic', + validForDuration: '1 lightyear', }, { principalName: 'user2@REALM2', @@ -24,6 +25,7 @@ isManaged: false, passwordWasRemembered: true, pic: 'pic2', + validForDuration: 'zero googolseconds', }, { principalName: 'user3@REALM3', @@ -33,6 +35,7 @@ isManaged: true, passwordWasRemembered: true, pic: 'pic2', + validForDuration: 'one over inf seconds', } ]; @@ -68,6 +71,7 @@ /** @override */ removeAccount(account) { this.methodCalled('removeAccount', account); + return Promise.resolve(settings.KerberosErrorType.kNone); } /** @override */ @@ -138,6 +142,33 @@ }); }); + test('AccountListSignedInSignedOutLabels', function() { + return browserProxy.whenCalled('getAccounts').then(() => { + Polymer.dom.flush(); + accountList = + kerberosAccounts.shadowRoot.querySelectorAll('.account-list-item'); + assertEquals(testAccounts.length, accountList.length); + + // Show 'Valid for <duration>' for accounts that are signed in. + let signedIn = accountList[0].querySelector('.signed-in'); + let signedOut = accountList[0].querySelector('.signed-out'); + assertTrue(testAccounts[0].isSignedIn); + assertFalse(signedIn.hidden); + assertTrue(signedOut.hidden); + assertEquals( + 'Valid for ' + testAccounts[0].validForDuration, + signedIn.innerText); + + // Show 'Expired' for accounts that are not signed in. + signedIn = accountList[1].querySelector('.signed-in'); + signedOut = accountList[1].querySelector('.signed-out'); + assertFalse(testAccounts[1].isSignedIn); + assertTrue(signedIn.hidden); + assertFalse(signedOut.hidden); + assertEquals('Expired', signedOut.innerText); + }); + }); + test('AddAccount', function() { assertTrue(!kerberosAccounts.$$('kerberos-add-account-dialog')); kerberosAccounts.$$('#add-account-button').click(); @@ -220,6 +251,24 @@ }); }); + test('RemoveAccountShowsToast', function(done) { + const toast = kerberosAccounts.$$('#account-removed-toast'); + assertTrue(!!toast); + assertFalse(toast.open); + + browserProxy.whenCalled('getAccounts').then(() => { + Polymer.dom.flush(); + clickMoreActions(Account.FIRST, MoreActions.REMOVE_ACCOUNT); + browserProxy.whenCalled('removeAccount').then(() => { + setTimeout(() => { + Polymer.dom.flush(); + assertTrue(toast.open); + done(); + }); + }); + }); + }); + test('AccountListIsUpdatedWhenKerberosAccountsUpdates', function() { assertEquals(1, browserProxy.getCallCount('getAccounts')); cr.webUIListenerCallback('kerberos-accounts-changed'); @@ -253,17 +302,23 @@ for (let i = 0; i < testAccounts.length; i++) { // Assert account has policy indicator iff account is managed. - const hasPolicyIndicator = + const hasAccountPolicyIndicator = !!accountList[i].querySelector('.account-policy-indicator'); - assertEquals(testAccounts[i].isManaged, hasPolicyIndicator); + assertEquals(testAccounts[i].isManaged, hasAccountPolicyIndicator); // Assert 'Remove' button is disabled iff account is managed. accountList[i].querySelector('.more-actions').click(); const moreActions = kerberosAccounts.$$('cr-action-menu').querySelectorAll('button'); - const removeAccountDisabled = - moreActions[MoreActions.REMOVE_ACCOUNT].disabled; - assertEquals(testAccounts[i].isManaged, removeAccountDisabled); + const removeAccountButton = moreActions[MoreActions.REMOVE_ACCOUNT]; + assertEquals(testAccounts[i].isManaged, removeAccountButton.disabled); + + // Assert 'Remove' button has policy indicator iff account is managed. + Polymer.dom.flush(); + const hasRemovalPolicyIndicator = !!removeAccountButton.querySelector( + '#remove-account-policy-indicator'); + assertEquals(testAccounts[i].isManaged, hasRemovalPolicyIndicator); + kerberosAccounts.$$('cr-action-menu').close(); } }); @@ -299,8 +354,9 @@ let password = null; let rememberPassword = null; let advancedConfigButton = null; - let addButton = null; + let actionButton = null; let generalError = null; + let title = null; // Indices of 'addAccount' params. const AddParams = { @@ -346,11 +402,14 @@ advancedConfigButton = dialog.$.advancedConfigButton; assertTrue(!!advancedConfigButton); - addButton = addDialog.querySelector('.action-button'); - assertTrue(!!addButton); + actionButton = addDialog.querySelector('.action-button'); + assertTrue(!!actionButton); generalError = dialog.$['general-error-message']; assertTrue(!!generalError); + + title = dialog.$$('[slot=title]').innerText; + assertTrue(!!title); } // Sets |error| as error result for addAccount(), simulates a click on the @@ -360,7 +419,7 @@ Polymer.dom.flush(); assertEquals(0, errorElement.innerText.length); browserProxy.addAccountError = error; - addButton.click(); + actionButton.click(); return browserProxy.whenCalled('addAccount').then(function() { Polymer.dom.flush(); assertNotEquals(0, errorElement.innerText.length); @@ -393,6 +452,8 @@ // Verifies expected states if no account is preset. test('StatesWithoutPresetAccount', function() { + assertTrue(title.startsWith('Add')); + assertEquals('Add', actionButton.innerText); assertFalse(username.disabled); assertEquals('', username.value); assertEquals('', password.value); @@ -403,6 +464,8 @@ // Verifies expected states if an account is preset. test('StatesWithPresetAccount', function() { createDialog(testAccounts[0]); + assertTrue(title.startsWith('Refresh')); + assertEquals('Refresh', actionButton.innerText); assertTrue(username.readonly); assertEquals(testAccounts[0].principalName, username.value); assertConfig(testAccounts[0].config); @@ -454,9 +517,9 @@ loadTimeData.overrideValues({kerberosRememberPasswordEnabled: true}); }); - // By clicking the "Add account", all field values are passed to the + // By clicking the action button, all field values are passed to the // 'addAccount' browser proxy method. - test('AddButtonPassesFieldValues', function() { + test('ActionButtonPassesFieldValues', function() { const EXPECTED_USER = 'testuser'; const EXPECTED_PASS = 'testpass'; const EXPECTED_REMEMBER_PASS = true; @@ -467,8 +530,8 @@ setConfig(EXPECTED_CONFIG); rememberPassword.checked = EXPECTED_REMEMBER_PASS; - assertFalse(addButton.disabled); - addButton.click(); + assertFalse(actionButton.disabled); + actionButton.click(); return browserProxy.whenCalled('addAccount').then(function(args) { assertEquals(EXPECTED_USER, args[AddParams.PRINCIPAL_NAME]); assertEquals(EXPECTED_PASS, args[AddParams.PASSWORD]); @@ -485,19 +548,19 @@ test('AllowExistingIsTrueForPresetAccounts', function() { // Populate dialog with preset account. createDialog(testAccounts[1]); - addButton.click(); + actionButton.click(); return browserProxy.whenCalled('addAccount').then(function(args) { assertTrue(args[AddParams.ALLOW_EXISTING]); }); }); - // While an account is being added, the "Add account" is disabled. - test('AddButtonDisableWhileInProgress', function() { - assertFalse(addButton.disabled); - addButton.click(); - assertTrue(addButton.disabled); + // While an account is being added, the action button is disabled. + test('ActionButtonDisableWhileInProgress', function() { + assertFalse(actionButton.disabled); + actionButton.click(); + assertTrue(actionButton.disabled); return browserProxy.whenCalled('addAccount').then(function(args) { - assertFalse(addButton.disabled); + assertFalse(actionButton.disabled); }); }); @@ -506,7 +569,7 @@ test('SubmitsEmptyPasswordIfRememberedPasswordIsUsed', function() { assertTrue(testAccounts[1].passwordWasRemembered); createDialog(testAccounts[1]); - addButton.click(); + actionButton.click(); return browserProxy.whenCalled('addAccount').then(function(args) { assertEquals('', args[AddParams.PASSWORD]); assertTrue(args[AddParams.REMEMBER_PASSWORD]); @@ -514,13 +577,14 @@ }); // If the account has passwordWasRemembered == true and the user changes the - // password before clicking 'Add' button, the changed password is submitted. + // password before clicking the action button, the changed password is + // submitted. test('SubmitsChangedPasswordIfRememberedPasswordIsChanged', function() { assertTrue(testAccounts[1].passwordWasRemembered); createDialog(testAccounts[1]); password.inputElement.value = 'some edit'; password.dispatchEvent(new CustomEvent('input')); - addButton.click(); + actionButton.click(); return browserProxy.whenCalled('addAccount').then(function(args) { assertNotEquals('', args[AddParams.PASSWORD]); assertTrue(args[AddParams.REMEMBER_PASSWORD]);
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn index 3e36a22..67439dd 100644 --- a/chromeos/BUILD.gn +++ b/chromeos/BUILD.gn
@@ -24,6 +24,7 @@ configs += [ ":chromeos_implementation", "//build/config/linux/nss:system_nss_no_ssl_config", + "//build/config/compiler:use_orderfile_for_hugepage", ] public_deps = [ "//chromeos/constants",
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd index bb4d11a..3162a98 100644 --- a/chromeos/chromeos_strings.grd +++ b/chromeos/chromeos_strings.grd
@@ -232,17 +232,14 @@ </message> <!-- Kerberos ticket expiry notifications --> - <message name="IDS_KERBEROS_TICKET_EXPIRY_DISPLAY_SOURCE" desc="Display source of the notification shown when a Kerberos ticket is about to expire (shown on the top of the notification)."> - Kerberos ticket expiring - </message> <message name="IDS_KERBEROS_TICKET_EXPIRY_TITLE" desc="Title of the notification shown when a Kerberos ticket is about to expire (shown below the display source and above the body)."> - Your Kerberos ticket is expiring + Kerberos ticket expires soon </message> <message name="IDS_KERBEROS_TICKET_EXPIRY_BODY" desc="Body of the notification shown when a Kerberos ticket is about to expire (shown below the title)."> - Please refresh the ticket for <ph name="PRINCIPAL_NAME">$1<ex>user@EXAMPLE.COM</ex></ph> soon + Refresh ticket for <ph name="PRINCIPAL_NAME">$1<ex>user@EXAMPLE.COM</ex></ph> </message> <message name="IDS_KERBEROS_TICKET_EXPIRY_BUTTON" desc="Button label of the notification shown when a Kerberos ticket is about to expire."> - REFRESH TICKET + REFRESH </message> <message name="IDS_IME_SERVICE_DISPLAY_NAME" desc="The display name (in the system task manager, etc) of the service process providing the input methods.">
diff --git a/chromeos/chromeos_strings_grd/IDS_KERBEROS_TICKET_EXPIRY_DISPLAY_SOURCE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_KERBEROS_TICKET_EXPIRY_DISPLAY_SOURCE.png.sha1 deleted file mode 100644 index c7fdbb8..0000000 --- a/chromeos/chromeos_strings_grd/IDS_KERBEROS_TICKET_EXPIRY_DISPLAY_SOURCE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -6ff127c0b94aac76132b5568a95d10c34390e130 \ No newline at end of file
diff --git a/chromeos/components/drivefs/BUILD.gn b/chromeos/components/drivefs/BUILD.gn index 1410789..16c1846 100644 --- a/chromeos/components/drivefs/BUILD.gn +++ b/chromeos/components/drivefs/BUILD.gn
@@ -76,6 +76,7 @@ "//base/test:test_support", "//chromeos/components/drivefs/mojom", "//chromeos/disks:test_support", + "//components/account_id", "//components/drive", "//components/invalidation/impl:test_support", "//mojo/public/cpp/bindings",
diff --git a/chromeos/components/drivefs/drivefs_auth.cc b/chromeos/components/drivefs/drivefs_auth.cc index 296f0b7..3b5991d 100644 --- a/chromeos/components/drivefs/drivefs_auth.cc +++ b/chromeos/components/drivefs/drivefs_auth.cc
@@ -59,7 +59,9 @@ &DriveFsAuth::AccountReady, weak_ptr_factory_.GetWeakPtr())); } -void DriveFsAuth::AccountReady(const CoreAccountInfo& info, +void DriveFsAuth::AccountReady(const CoreAccountId& account_id, + const std::string& gaia, + const std::string& email, const identity::AccountState& state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); weak_ptr_factory_.InvalidateWeakPtrs();
diff --git a/chromeos/components/drivefs/drivefs_auth.h b/chromeos/components/drivefs/drivefs_auth.h index 94d4c6cc..8346bf0e 100644 --- a/chromeos/components/drivefs/drivefs_auth.h +++ b/chromeos/components/drivefs/drivefs_auth.h
@@ -72,7 +72,9 @@ mojom::DriveFsDelegate::GetAccessTokenCallback callback); private: - void AccountReady(const CoreAccountInfo& info, + void AccountReady(const CoreAccountId& account_id, + const std::string& gaia, + const std::string& email, const identity::AccountState& state); void GotChromeAccessToken(const base::Optional<std::string>& access_token,
diff --git a/chromeos/components/drivefs/drivefs_auth_unittest.cc b/chromeos/components/drivefs/drivefs_auth_unittest.cc index 7eeecdda..075f697 100644 --- a/chromeos/components/drivefs/drivefs_auth_unittest.cc +++ b/chromeos/components/drivefs/drivefs_auth_unittest.cc
@@ -12,6 +12,7 @@ #include "base/test/scoped_task_environment.h" #include "base/test/simple_test_clock.h" #include "base/timer/mock_timer.h" +#include "components/account_id/account_id.h" #include "services/identity/public/mojom/constants.mojom.h" #include "services/identity/public/mojom/identity_accessor.mojom-test-utils.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -107,11 +108,9 @@ return; } auto account_id = AccountId::FromUserEmailGaiaId("test@example.com", "ID"); - CoreAccountInfo account_info; - account_info.email = account_id.GetUserEmail(); - account_info.gaia = account_id.GetGaiaId(); - account_info.account_id = account_id.GetAccountIdKey(); - std::move(callback).Run(account_info, {}); + std::move(callback).Run(CoreAccountId(account_id.GetAccountIdKey()), + account_id.GetGaiaId(), account_id.GetUserEmail(), + {}); } void GetAccessToken(const CoreAccountId& account_id,
diff --git a/chromeos/components/drivefs/drivefs_host_unittest.cc b/chromeos/components/drivefs/drivefs_host_unittest.cc index 0cb6c56..93b590c 100644 --- a/chromeos/components/drivefs/drivefs_host_unittest.cc +++ b/chromeos/components/drivefs/drivefs_host_unittest.cc
@@ -228,11 +228,9 @@ void GetPrimaryAccountWhenAvailable( GetPrimaryAccountWhenAvailableCallback callback) override { auto account_id = AccountId::FromUserEmailGaiaId("test@example.com", "ID"); - CoreAccountInfo account_info; - account_info.email = account_id.GetUserEmail(); - account_info.gaia = account_id.GetGaiaId(); - account_info.account_id = account_id.GetAccountIdKey(); - std::move(callback).Run(account_info, {}); + std::move(callback).Run(CoreAccountId(account_id.GetAccountIdKey()), + account_id.GetGaiaId(), account_id.GetUserEmail(), + {}); } void GetAccessToken(const CoreAccountId& account_id,
diff --git a/chromeos/printing/ppd_provider.cc b/chromeos/printing/ppd_provider.cc index 7958605..44b035d 100644 --- a/chromeos/printing/ppd_provider.cc +++ b/chromeos/printing/ppd_provider.cc
@@ -491,9 +491,17 @@ RunPpdReferenceResolutionSucceeded(std::move(next.cb), kEpsonGenericPPD); } else { - // We don't have anything else left to try. We've reached unsupported - // USB printer, try to grab the manufacturer name. - ResolveUsbManufacturer(std::move(next.cb), search_data.usb_vendor_id); + // We don't have anything else left to try. + if (search_data.discovery_type == + PrinterSearchData::PrinterDiscoveryType::kUsb) { + // We've reached unsupported USB printer, try to grab the manufacturer + // name. + ResolveUsbManufacturer(std::move(next.cb), search_data.usb_vendor_id); + } else { + // Non-USB printer, so we fail resolution normally. + RunPpdReferenceResolutionNotFound(std::move(next.cb), + "" /* Empty Manufacturer */); + } } } // Didn't start any fetches.
diff --git a/chromeos/printing/ppd_provider_unittest.cc b/chromeos/printing/ppd_provider_unittest.cc index 3ea200b..e00bb23b 100644 --- a/chromeos/printing/ppd_provider_unittest.cc +++ b/chromeos/printing/ppd_provider_unittest.cc
@@ -954,6 +954,7 @@ auto provider = CreateProvider("en", false); PrinterSearchData search_data; + search_data.discovery_type = PrinterSearchData::PrinterDiscoveryType::kUsb; // Vendor id that exists, nonexistent device id, should get a NOT_FOUND. // Although this is an unsupported printer model, we can still expect to get
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn index 1fb5132..75fdc06 100644 --- a/chromeos/services/assistant/BUILD.gn +++ b/chromeos/services/assistant/BUILD.gn
@@ -41,6 +41,7 @@ "//chromeos/dbus/power:power_manager_proto", "//chromeos/services/assistant/public/proto", "//components/account_id", + "//components/user_manager", "//services/device/public/mojom", "//services/identity/public/mojom", "//services/network/public/cpp:cpp",
diff --git a/chromeos/services/assistant/service.cc b/chromeos/services/assistant/service.cc index 9d8add4..6d62a07 100644 --- a/chromeos/services/assistant/service.cc +++ b/chromeos/services/assistant/service.cc
@@ -26,6 +26,7 @@ #include "chromeos/services/assistant/fake_assistant_manager_service_impl.h" #include "chromeos/services/assistant/fake_assistant_settings_manager_impl.h" #include "chromeos/services/assistant/public/features.h" +#include "components/user_manager/known_user.h" #include "google_apis/gaia/google_service_auth_error.h" #include "services/identity/public/cpp/scope_set.h" #include "services/identity/public/mojom/constants.mojom.h" @@ -289,22 +290,30 @@ } void Service::GetPrimaryAccountInfoCallback( - const base::Optional<CoreAccountInfo>& account_info, + const base::Optional<CoreAccountId>& account_id, + const base::Optional<std::string>& gaia, + const base::Optional<std::string>& email, const identity::AccountState& account_state) { - if (!account_info.has_value() || !account_state.has_refresh_token || - account_info.value().gaia.empty()) { + // Validate the remotely-supplied parameters before using them below: if + // |account_id| is non-null, the other two should be non-null as well per + // the stated contract of IdentityAccessor::GetPrimaryAccountInfo(). + CHECK((!account_id.has_value() || (gaia.has_value() && email.has_value()))); + + if (!account_id.has_value() || !account_state.has_refresh_token || + gaia->empty()) { LOG(ERROR) << "Failed to retrieve primary account info."; RetryRefreshToken(); return; } - account_id_ = AccountIdFromAccountInfo(account_info.value()); + account_id_ = user_manager::known_user::GetAccountId(*email, *gaia, + AccountType::GOOGLE); identity::ScopeSet scopes; scopes.insert(kScopeAssistant); scopes.insert(kScopeAuthGcm); if (features::IsClearCutLogEnabled()) scopes.insert(kScopeClearCutLog); identity_accessor_->GetAccessToken( - account_info.value().account_id, scopes, "cros_assistant", + *account_id, scopes, "cros_assistant", base::BindOnce(&Service::GetAccessTokenCallback, base::Unretained(this))); }
diff --git a/chromeos/services/assistant/service.h b/chromeos/services/assistant/service.h index 846a9333..4d1981cf 100644 --- a/chromeos/services/assistant/service.h +++ b/chromeos/services/assistant/service.h
@@ -148,7 +148,9 @@ identity::mojom::IdentityAccessor* GetIdentityAccessor(); void GetPrimaryAccountInfoCallback( - const base::Optional<CoreAccountInfo>& account_info, + const base::Optional<CoreAccountId>& account_id, + const base::Optional<std::string>& gaia, + const base::Optional<std::string>& email, const identity::AccountState& account_state); void GetAccessTokenCallback(const base::Optional<std::string>& token,
diff --git a/chromeos/services/assistant/service_unittest.cc b/chromeos/services/assistant/service_unittest.cc index 0e69aa1..7b88a2c 100644 --- a/chromeos/services/assistant/service_unittest.cc +++ b/chromeos/services/assistant/service_unittest.cc
@@ -57,16 +57,15 @@ private: // identity::mojom::IdentityAccessor: void GetPrimaryAccountInfo(GetPrimaryAccountInfoCallback callback) override { - CoreAccountInfo account_info; - account_info.account_id = "account_id"; - account_info.gaia = "fakegaiaid"; - account_info.email = "fake@email"; + CoreAccountId account_id("account_id"); + std::string gaia = "fakegaiaid"; + std::string email = "fake@email"; identity::AccountState account_state; account_state.has_refresh_token = true; account_state.is_primary_account = true; - std::move(callback).Run(account_info, account_state); + std::move(callback).Run(account_id, gaia, email, account_state); } void GetPrimaryAccountWhenAvailable(
diff --git a/components/BUILD.gn b/components/BUILD.gn index ba91040d..84f29bf 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -435,6 +435,7 @@ "//content/public/test/android:content_java_test_support", "//content/shell/android:content_shell_browsertests_java", "//testing/android/native_test:native_test_java", + "//ui/android:ui_full_java", ] java_files = [ "test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsActivity.java",
diff --git a/components/autofill/content/renderer/page_passwords_analyser.cc b/components/autofill/content/renderer/page_passwords_analyser.cc index b40d2b62..91c67ea 100644 --- a/components/autofill/content/renderer/page_passwords_analyser.cc +++ b/components/autofill/content/renderer/page_passwords_analyser.cc
@@ -4,7 +4,10 @@ #include "components/autofill/content/renderer/page_passwords_analyser.h" +#include <map> #include <stack> +#include <string> +#include <vector> #include "base/lazy_instance.h" #include "base/strings/stringprintf.h" @@ -111,7 +114,8 @@ const re2::RE2* regex; size_t match; - InputHint(const re2::RE2* regex) : regex(regex), match(std::string::npos) {} + explicit InputHint(const re2::RE2* regex) + : regex(regex), match(std::string::npos) {} void MatchLabel(std::string& label_content, size_t index) { if (re2::RE2::FullMatch(label_content, *regex)) @@ -151,15 +155,15 @@ // example if an invalid attribute is added), but these cases are assumed // to be rare, and are ignored for the sake of simplicity. // The id of |node| will additionally be added to the corresponding |ids| set. -bool TrackElementIfUntracked( - const blink::WebElement& node, - std::set<blink::WebNode>* skip_nodes, +bool TrackElementByRendererIdIfUntracked( + const blink::WebElement& element, + const uint32_t renderer_id, + std::set<uint32_t>* skip_renderer_ids, std::map<std::string, std::vector<blink::WebNode>>* nodes_for_id) { - if (skip_nodes->count(node)) + if (skip_renderer_ids->count(renderer_id)) return true; - skip_nodes->insert(node); - // If we don't skip the node, we want to make sure its id is tracked. - TrackElementId(node, nodes_for_id); + skip_renderer_ids->insert(renderer_id); + TrackElementId(element, nodes_for_id); return false; } @@ -168,7 +172,8 @@ // analysis. std::vector<FormInputCollection> ExtractFormsForAnalysis( const blink::WebDocument& document, - std::set<blink::WebNode>* skip_nodes, + std::set<uint32_t>* skip_form_ids, + std::set<uint32_t>* skip_control_ids, PageFormAnalyserLogger* logger) { std::vector<FormInputCollection> form_input_collections; @@ -185,7 +190,9 @@ blink::WebVector<blink::WebFormControlElement> form_control_elements; form.GetFormControlElements(form_control_elements); for (const blink::WebFormControlElement& input : form_control_elements) { - if (TrackElementIfUntracked(input, skip_nodes, &nodes_for_id)) + if (TrackElementByRendererIdIfUntracked( + input, input.UniqueRendererFormControlId(), skip_control_ids, + &nodes_for_id)) continue; // We are only interested in a subset of input elements -- those likely // to be username or password fields. @@ -197,14 +204,18 @@ inputs_with_forms.insert(input); } } - - TrackElementIfUntracked(form, skip_nodes, &nodes_for_id); + TrackElementByRendererIdIfUntracked(form, form.UniqueRendererFormId(), + skip_form_ids, &nodes_for_id); } // Check for password fields that are not contained inside forms. auto password_inputs = document.QuerySelectorAll("input[type=\"password\"]"); for (unsigned i = 0; i < password_inputs.size(); ++i) { - if (TrackElementIfUntracked(password_inputs[i], skip_nodes, &nodes_for_id)) + if (TrackElementByRendererIdIfUntracked( + password_inputs[i], + blink::ToWebInputElement(&password_inputs[i]) + ->UniqueRendererFormControlId(), + skip_control_ids, &nodes_for_id)) continue; // Any password fields inside <form> elements will have been skipped, // leaving just those without associated forms. @@ -221,7 +232,10 @@ auto text_inputs = document.QuerySelectorAll(blink::WebString::FromUTF8(selector)); for (const blink::WebElement& text_input : text_inputs) - TrackElementIfUntracked(text_input, skip_nodes, &nodes_for_id); + TrackElementByRendererIdIfUntracked( + text_input, + blink::ToWebInputElement(&text_input)->UniqueRendererFormControlId(), + skip_control_ids, &nodes_for_id); // Warn against elements sharing an id attribute. Duplicate id attributes both // are against the HTML specification and can cause issues with password @@ -419,7 +433,8 @@ PagePasswordsAnalyser::~PagePasswordsAnalyser() {} void PagePasswordsAnalyser::Reset() { - skip_nodes_.clear(); + skip_control_element_renderer_ids_.clear(); + skip_form_element_renderer_ids_.clear(); } void PagePasswordsAnalyser::AnalyseDocumentDOM(blink::WebLocalFrame* frame, @@ -429,7 +444,8 @@ blink::WebDocument document(frame->GetDocument()); // Extract all the forms from the DOM, and provide relevant warnings. std::vector<FormInputCollection> forms( - ExtractFormsForAnalysis(document, &skip_nodes_, logger)); + ExtractFormsForAnalysis(document, &skip_form_element_renderer_ids_, + &skip_control_element_renderer_ids_, logger)); // Analyse each form in turn, for example with respect to autocomplete // attributes.
diff --git a/components/autofill/content/renderer/page_passwords_analyser.h b/components/autofill/content/renderer/page_passwords_analyser.h index ed418c1..6c7d980f 100644 --- a/components/autofill/content/renderer/page_passwords_analyser.h +++ b/components/autofill/content/renderer/page_passwords_analyser.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_PASSWORDS_ANALYSER_H_ #define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_PASSWORDS_ANALYSER_H_ +#include <set> + #include "third_party/blink/public/web/web_console_message.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -40,7 +42,9 @@ // By default, the analyser will log to the DevTools console. void AnalyseDocumentDOM(blink::WebLocalFrame* frame); - std::set<blink::WebNode> skip_nodes_; + // A set of renderer_ids which have already been analyzed. + std::set<uint32_t> skip_control_element_renderer_ids_; + std::set<uint32_t> skip_form_element_renderer_ids_; // This is true when new DOM content is available since the last time // the page was analysed, meaning the page needs to be reanalysed. bool page_dirty_; @@ -48,5 +52,4 @@ } // namespace autofill -#endif - // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_PASSWORDS_ANALYSER_H_ +#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_PASSWORDS_ANALYSER_H_
diff --git a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc index f068bd83..79b9ebc 100644 --- a/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc +++ b/components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl_unittest.cc
@@ -246,62 +246,6 @@ AutofillMetrics::UNMASK_PROMPT_UNMASKED_CARD_AFTER_FAILED_ATTEMPTS, 1); } -TEST_F(CardUnmaskPromptControllerImplTest, LogSavedCardLocally) { - ShowPromptAndSimulateResponse(true); - base::HistogramTester histogram_tester; - - controller_->OnVerificationResult(AutofillClient::SUCCESS); - controller_->OnUnmaskDialogClosed(); - - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.Events", - AutofillMetrics::UNMASK_PROMPT_SAVED_CARD_LOCALLY, 1); -} - -TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptIn) { - SetImportCheckboxState(false); - ShowPromptAndSimulateResponse(true); - base::HistogramTester histogram_tester; - controller_->OnUnmaskDialogClosed(); - - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.Events", - AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_IN, 1); -} - -TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptIn) { - SetImportCheckboxState(false); - ShowPromptAndSimulateResponse(false); - base::HistogramTester histogram_tester; - controller_->OnUnmaskDialogClosed(); - - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.Events", - AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_IN, 1); -} - -TEST_F(CardUnmaskPromptControllerImplTest, LogDidOptOut) { - SetImportCheckboxState(true); - ShowPromptAndSimulateResponse(false); - base::HistogramTester histogram_tester; - controller_->OnUnmaskDialogClosed(); - - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.Events", - AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_OPT_OUT, 1); -} - -TEST_F(CardUnmaskPromptControllerImplTest, LogDidNotOptOut) { - SetImportCheckboxState(true); - ShowPromptAndSimulateResponse(true); - base::HistogramTester histogram_tester; - controller_->OnUnmaskDialogClosed(); - - histogram_tester.ExpectBucketCount( - "Autofill.UnmaskPrompt.Events", - AutofillMetrics::UNMASK_PROMPT_LOCAL_SAVE_DID_NOT_OPT_OUT, 1); -} - TEST_F(CardUnmaskPromptControllerImplTest, DontLogForHiddenCheckbox) { controller_->set_can_store_locally(false); ShowPromptAndSimulateResponse(false);
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index 9522e53..696b94b 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -92,12 +92,12 @@ // When enabled, will remove the option to save unmasked server cards as // FULL_SERVER_CARDs upon successful unmask. const base::Feature kAutofillNoLocalSaveOnUnmaskSuccess{ - "AutofillNoLocalSaveOnUnmaskSuccess", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillNoLocalSaveOnUnmaskSuccess", base::FEATURE_ENABLED_BY_DEFAULT}; // When enabled, no local copy of server card will be saved when upload // succeeds. const base::Feature kAutofillNoLocalSaveOnUploadSuccess{ - "AutofillNoLocalSaveOnUploadSuccess", base::FEATURE_DISABLED_BY_DEFAULT}; + "AutofillNoLocalSaveOnUploadSuccess", base::FEATURE_ENABLED_BY_DEFAULT}; // When enabled, local and upload credit card save dialogs will be updated to // new M72 guidelines, including a [No thanks] cancel button and an extended
diff --git a/components/browsing_data/core/browsing_data_utils_unittest.cc b/components/browsing_data/core/browsing_data_utils_unittest.cc index 6b292df..defe1227 100644 --- a/components/browsing_data/core/browsing_data_utils_unittest.cc +++ b/components/browsing_data/core/browsing_data_utils_unittest.cc
@@ -115,17 +115,17 @@ } kTestCases[] = { {0, false, {}, "None"}, {0, true, {}, "None"}, - {1, false, {"domain1.com"}, "1 password for domain1.com"}, - {1, true, {"domain1.com"}, "1 password for domain1.com (synced)"}, + {1, false, {"domain1.com"}, "1 password (for domain1.com)"}, + {1, true, {"domain1.com"}, "1 password (for domain1.com, synced)"}, {5, false, {"domain1.com", "domain2.com", "domain3.com", "domain4.com"}, - "5 passwords for domain1.com, domain2.com, and 3 more"}, + "5 passwords (for domain1.com, domain2.com, and 3 more)"}, {5, true, {"domain1.com", "domain2.com", "domain3.com", "domain4.com", "domain5.com"}, - "5 passwords for domain1.com, domain2.com, and 3 more (synced)"}, + "5 passwords (for domain1.com, domain2.com, and 3 more, synced)"}, }; for (const TestCase& test_case : kTestCases) {
diff --git a/components/browsing_data_strings.grdp b/components/browsing_data_strings.grdp index 1ab6cca3..b3bc9079 100644 --- a/components/browsing_data_strings.grdp +++ b/components/browsing_data_strings.grdp
@@ -34,16 +34,16 @@ <message name="IDS_DEL_PASSWORDS_COUNTER" desc="A counter showing how many passwords the user has. The list of example domains associated with the user's passwords is denoted by the placeholder DOMAIN_LIST."> {COUNT, plural, =0 {None} - =1 {1 password for <ph name="DOMAIN_LIST">$1<ex>google.com</ex></ph>} - =2 {2 passwords for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com</ex></ph>} - other {# passwords for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com, and 2 more</ex></ph>}} + =1 {1 password (for <ph name="DOMAIN_LIST">$1<ex>google.com</ex></ph>)} + =2 {2 passwords (for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com</ex></ph>)} + other {# passwords (for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com, and 2 more</ex></ph>)}} </message> <message name="IDS_DEL_PASSWORDS_COUNTER_SYNCED" desc="A counter showing how many passwords the user has and that they are synced. The list of example domains associated with the user's passwords is denoted by the placeholder DOMAIN_LIST."> {COUNT, plural, =0 {None} - =1 {1 password for <ph name="DOMAIN_LIST">$1<ex>google.com</ex></ph> (synced)} - =2 {2 passwords for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com</ex></ph> (synced)} - other {# passwords for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com, and 2 more</ex></ph> (synced)}} + =1 {1 password (for <ph name="DOMAIN_LIST">$1<ex>google.com</ex></ph>, synced)} + =2 {2 passwords (for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com</ex></ph>, synced)} + other {# passwords (for <ph name="DOMAIN_LIST">$1<ex>google.com, chrome.com, and 2 more</ex></ph>, synced)}} </message> <message name="IDS_DEL_PASSWORDS_DOMAINS_DISPLAY" desc="This message is shown alongside the passwords count displaying example domains. If there is at least one password, we will show at most two example domain names, denoted by the placeholders EXAMPLE_DOMAIN_1 and EXAMPLE_DOMAIN_2. If there are more than two domains, we will also say 'and X more.'"> {COUNT, plural,
diff --git a/components/chrome_cleaner/public/interfaces/OWNERS b/components/chrome_cleaner/public/interfaces/OWNERS index 7b93fe80..74b69f8 100644 --- a/components/chrome_cleaner/public/interfaces/OWNERS +++ b/components/chrome_cleaner/public/interfaces/OWNERS
@@ -1,6 +1,3 @@ -csharp@chromium.org -robertshield@chromium.org - per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/chrome_cleaner/public/proto/BUILD.gn b/components/chrome_cleaner/public/proto/BUILD.gn new file mode 100644 index 0000000..d24d95e5 --- /dev/null +++ b/components/chrome_cleaner/public/proto/BUILD.gn
@@ -0,0 +1,11 @@ +# Copyright 2019 The Chromium Authors. All Rights Reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("proto") { + sources = [ + "chrome_prompt.proto", + ] +}
diff --git a/components/chrome_cleaner/public/proto/OWNERS b/components/chrome_cleaner/public/proto/OWNERS new file mode 100644 index 0000000..a0c8c42 --- /dev/null +++ b/components/chrome_cleaner/public/proto/OWNERS
@@ -0,0 +1,7 @@ +# Require security review on .proto file changes since they define an IPC +# interface. +per-file *.proto=set noparent +per-file *.proto=file://ipc/SECURITY_OWNERS + +# TEAM: security-dev@chromium.org +# COMPONENT: UI>Browser>Preferences>Protector
diff --git a/components/chrome_cleaner/public/proto/chrome_prompt.proto b/components/chrome_cleaner/public/proto/chrome_prompt.proto new file mode 100644 index 0000000..de5500d2 --- /dev/null +++ b/components/chrome_cleaner/public/proto/chrome_prompt.proto
@@ -0,0 +1,91 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +package chrome_cleaner; + +// Request messages are sent from the Chrome Cleaner to Chrome. +// Response messages are sent from Chrome to the Chrome Cleaner. + +// Tells Chrome to close the IPC connection to the cleaner. There is no +// response to this request. +message CloseConnectionRequest {} + +message QueryCapabilityRequest { + // Add “optional bool supports_foo” fields to this message for each capability + // that may or may not be supported by Chrome. +} + +message QueryCapabilityResponse { + // Add a matching “optional bool supports_foo” field to this message for each + // field in QueryCapabilityRequest. +} + +// Tells Chrome to prompt the user to start a cleanup because the Chrome +// Cleanup tool has detected unwanted software on the system. +message PromptUserRequest { + // List of fully-qualified paths of the files that will be deleted by the + // Chrome Cleanup tool. + repeated string files_to_delete = 1; + + // List of fully-qualified paths of the registry keys that will be changed or + // deleted by the Chrome Cleanup tool. These are formatted for display to the + // user (eg. "HKCU:32\software\unwanted_software"). + repeated string registry_keys = 2; + + // List of IDs of extensions that will be removed by the Chrome Cleanup tool. + // These are 32-character extension ID's. + repeated string extension_ids = 3; + + // After deserializing, Chrome must check if + // PromptUserRequest.unknown_fields() is empty. If the request includes any + // fields that Chrome does not know how to display it will immediately return + // DENIED instead of showing the prompt. +} + +// Tells the Chrome Cleanup tool whether the user accepted the prompt. +message PromptUserResponse { + enum PromptAcceptance { + UNSPECIFIED = 0; + // The user explicitly accepted the cleanup operation and cleaner logs + // upload is allowed. + ACCEPTED_WITH_LOGS = 1; + // The user explicitly accepted the cleanup operation and cleaner logs + // upload is not allowed. + ACCEPTED_WITHOUT_LOGS = 2; + // The user explicitly denied the Chrome prompt. + DENIED = 3; + }; + + // Missing or UNSPECIFIED |prompt_acceptance| will cause Chrome Cleanup to + // exit with an error. + optional PromptAcceptance prompt_acceptance = 1 [default = UNSPECIFIED]; +} + +// Tells Chrome to remove the given extensions. +message RemoveExtensionsRequest { + repeated string extension_ids = 1; +} + +// Tells the Chrome Cleanup tool whether Chrome successfully removed all +// extensions. +message RemoveExtensionsResponse { + // Missing |success| will cause Chrome Cleanup to exit with an error. + optional bool success = 1; +} + +message ChromePromptRequest { + oneof request { + CloseConnectionRequest close_connection = 1; + QueryCapabilityRequest query_capability = 2; + PromptUserRequest prompt_user = 3; + RemoveExtensionsRequest remove_extensions = 4; + // An unrecognized |request| will cause Chrome to kill the Chrome Cleanup + // process, since it has no response message to return. Chrome Cleanup + // should send QueryCapabilityRequest to see if Chrome supports requests + // that aren’t in the first interface version. + } +}
diff --git a/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/components/dom_distiller/core/javascript/dom_distiller_viewer.js index f3ef2ed..e01f6133 100644 --- a/components/dom_distiller/core/javascript/dom_distiller_viewer.js +++ b/components/dom_distiller/core/javascript/dom_distiller_viewer.js
@@ -58,46 +58,53 @@ document.body.setAttribute('dir', direction); } -// Maps JS Font Family to CSS class and then changes body class name. -// CSS classes must agree with distilledpage.css. -function useFontFamily(fontFamily) { - var cssClass; - if (fontFamily == 'serif') { - cssClass = 'serif'; - } else if (fontFamily == 'monospace') { - cssClass = 'monospace'; - } else { - cssClass = 'sans-serif'; - } - // Relies on the classname order of the body being Theme class, then Font - // Family class. - var themeClass = document.body.className.split(' ')[0]; - document.body.className = themeClass + ' ' + cssClass; +function removeAll(source, elementsToRemove) { + elementsToRemove.forEach(function(element) { + source.remove(element) + }); } -// Maps JS theme to CSS class and then changes body class name. -// CSS classes must agree with distilledpage.css. -function useTheme(theme) { - var cssClass; - if (theme == 'sepia') { - cssClass = 'sepia'; - } else if (theme == 'dark') { - cssClass = 'dark'; - } else { - cssClass = 'light'; +// These classes must agree with the font classes in distilledpage.css. +const fontFamilyClasses = ['sans-serif', 'serif', 'monospace']; +function getFontFamilyClass(fontFamily) { + if (fontFamilyClasses.includes(fontFamily)) { + return fontFamily; } - // Relies on the classname order of the body being Theme class, then Font - // Family class. - var fontFamilyClass = document.body.className.split(' ')[1]; - document.body.className = cssClass + ' ' + fontFamilyClass; + return fontFamilyClasses[0]; +} +function useFontFamily(fontFamily) { + removeAll(document.body.classList, fontFamilyClasses); + document.body.classList.add(getFontFamilyClass(fontFamily)); +} + +// These classes must agree with the theme classes in distilledpage.css. +const themeClasses = ['light', 'dark', 'sepia']; +function getThemeClass(theme) { + if (themeClasses.includes(theme)) { + return theme; + } + return themeClasses[0]; +} + +function useTheme(theme) { + removeAll(document.body.classList, themeClasses); + document.body.classList.add(getThemeClass(theme)); updateToolbarColor(); } +function getThemeFromElement(element) { + var foundTheme = themeClasses[0]; + themeClasses.forEach(function(theme) { + if (element.classList.contains(theme)) { + foundTheme = theme; + } + }); + return foundTheme; +} + function updateToolbarColor() { - // Relies on the classname order of the body being Theme class, then Font - // Family class. - var themeClass = document.body.className.split(' ')[0]; + var themeClass = getThemeFromElement(document.body); var toolbarColor; if (themeClass == 'sepia') {
diff --git a/components/flags_ui/resources/flags.html b/components/flags_ui/resources/flags.html index 84eff8c..bc99d2a 100644 --- a/components/flags_ui/resources/flags.html +++ b/components/flags_ui/resources/flags.html
@@ -6,6 +6,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> </if> +<title>Flags</title> <link rel="stylesheet" href="flags.css">
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.h b/components/gcm_driver/crypto/gcm_encryption_provider.h index e9134d2..b1c65f89 100644 --- a/components/gcm_driver/crypto/gcm_encryption_provider.h +++ b/components/gcm_driver/crypto/gcm_encryption_provider.h
@@ -38,8 +38,7 @@ public: // Callback to be invoked when the public key and auth secret are available. using EncryptionInfoCallback = - base::OnceCallback<void(const std::string& p256dh, - const std::string& auth_secret)>; + base::OnceCallback<void(std::string p256dh, std::string auth_secret)>; // Callback to be invoked when a message may have been decrypted, as indicated // by the |result|. The |message| contains the dispatchable message in success
diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc index 115109a9..02055c8 100644 --- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc +++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
@@ -79,10 +79,10 @@ // To be used as a callback for GCMEncryptionProvider::GetEncryptionInfo(). void DidGetEncryptionInfo(std::string* p256dh_out, std::string* auth_secret_out, - const std::string& p256dh, - const std::string& auth_secret) { - *p256dh_out = p256dh; - *auth_secret_out = auth_secret; + std::string p256dh, + std::string auth_secret) { + *p256dh_out = std::move(p256dh); + *auth_secret_out = std::move(auth_secret); } // To be used as a callback for GCMKeyStore::{GetKeys,CreateKeys}.
diff --git a/components/gcm_driver/gcm_driver.cc b/components/gcm_driver/gcm_driver.cc index 0ec1fc90..bdc4690 100644 --- a/components/gcm_driver/gcm_driver.cc +++ b/components/gcm_driver/gcm_driver.cc
@@ -151,11 +151,10 @@ SendImpl(app_id, receiver_id, message); } -void GCMDriver::GetEncryptionInfo( - const std::string& app_id, - const GetEncryptionInfoCallback& callback) { +void GCMDriver::GetEncryptionInfo(const std::string& app_id, + GetEncryptionInfoCallback callback) { encryption_provider_.GetEncryptionInfo(app_id, "" /* authorized_entity */, - callback); + std::move(callback)); } void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
diff --git a/components/gcm_driver/gcm_driver.h b/components/gcm_driver/gcm_driver.h index 33e0803..af3223b 100644 --- a/components/gcm_driver/gcm_driver.h +++ b/components/gcm_driver/gcm_driver.h
@@ -100,7 +100,7 @@ using SendCallback = base::Callback<void(const std::string& message_id, GCMClient::Result result)>; using GetEncryptionInfoCallback = - base::Callback<void(const std::string&, const std::string&)>; + base::OnceCallback<void(std::string p256dh, std::string auth_secret)>; using GetGCMStatisticsCallback = base::Callback<void(const GCMClient::GCMStatistics& stats)>; using SendWebPushMessageCallback = @@ -191,7 +191,7 @@ // created. The |callback| will be invoked when it is available. Only use with // GCM registrations; use InstanceID::GetEncryptionInfo for InstanceID tokens. virtual void GetEncryptionInfo(const std::string& app_id, - const GetEncryptionInfoCallback& callback); + GetEncryptionInfoCallback callback); const GCMAppHandlerMap& app_handlers() const { return app_handlers_; }
diff --git a/components/gcm_driver/gcm_driver_desktop_unittest.cc b/components/gcm_driver/gcm_driver_desktop_unittest.cc index 39239d8..b40a9721 100644 --- a/components/gcm_driver/gcm_driver_desktop_unittest.cc +++ b/components/gcm_driver/gcm_driver_desktop_unittest.cc
@@ -153,8 +153,7 @@ void RegisterCompleted(const std::string& registration_id, GCMClient::Result result); void SendCompleted(const std::string& message_id, GCMClient::Result result); - void GetEncryptionInfoCompleted(const std::string& p256dh, - const std::string& auth_secret); + void GetEncryptionInfoCompleted(std::string p256dh, std::string auth_secret); void UnregisterCompleted(GCMClient::Result result); const base::Closure& async_operation_completed_callback() const { @@ -311,8 +310,8 @@ base::RunLoop run_loop; async_operation_completed_callback_ = run_loop.QuitClosure(); driver_->GetEncryptionInfo( - app_id, base::Bind(&GCMDriverTest::GetEncryptionInfoCompleted, - base::Unretained(this))); + app_id, base::BindOnce(&GCMDriverTest::GetEncryptionInfoCompleted, + base::Unretained(this))); if (wait_to_finish == WAIT) run_loop.Run(); } @@ -350,10 +349,10 @@ async_operation_completed_callback_.Run(); } -void GCMDriverTest::GetEncryptionInfoCompleted(const std::string& p256dh, - const std::string& auth_secret) { - p256dh_ = p256dh; - auth_secret_ = auth_secret; +void GCMDriverTest::GetEncryptionInfoCompleted(std::string p256dh, + std::string auth_secret) { + p256dh_ = std::move(p256dh); + auth_secret_ = std::move(auth_secret); if (!async_operation_completed_callback_.is_null()) async_operation_completed_callback_.Run(); }
diff --git a/components/gcm_driver/gcm_driver_unittest.cc b/components/gcm_driver/gcm_driver_unittest.cc index 93ec392..b9735a04 100644 --- a/components/gcm_driver/gcm_driver_unittest.cc +++ b/components/gcm_driver/gcm_driver_unittest.cc
@@ -95,8 +95,7 @@ WaitToFinish wait_to_finish); void SendWebPushMessageCompleted(base::Optional<std::string> message_id); - void GetEncryptionInfoCompleted(const std::string& p256dh, - const std::string& auth_secret); + void GetEncryptionInfoCompleted(std::string p256dh, std::string auth_secret); void DecryptMessageCompleted(GCMDecryptionResult result, const IncomingMessage& message); void UnregisterCompleted(GCMClient::Result result); @@ -226,8 +225,8 @@ base::RunLoop run_loop; async_operation_completed_callback_ = run_loop.QuitClosure(); driver_->GetEncryptionInfo( - app_id, base::Bind(&GCMDriverBaseTest::GetEncryptionInfoCompleted, - base::Unretained(this))); + app_id, base::BindOnce(&GCMDriverBaseTest::GetEncryptionInfoCompleted, + base::Unretained(this))); if (wait_to_finish == WAIT) run_loop.Run(); } @@ -254,11 +253,10 @@ async_operation_completed_callback_.Run(); } -void GCMDriverBaseTest::GetEncryptionInfoCompleted( - const std::string& p256dh, - const std::string& auth_secret) { - p256dh_ = p256dh; - auth_secret_ = auth_secret; +void GCMDriverBaseTest::GetEncryptionInfoCompleted(std::string p256dh, + std::string auth_secret) { + p256dh_ = std::move(p256dh); + auth_secret_ = std::move(auth_secret); if (!async_operation_completed_callback_.is_null()) async_operation_completed_callback_.Run(); }
diff --git a/components/gcm_driver/instance_id/instance_id.cc b/components/gcm_driver/instance_id/instance_id.cc index ab91121f..9a75aae3 100644 --- a/components/gcm_driver/instance_id/instance_id.cc +++ b/components/gcm_driver/instance_id/instance_id.cc
@@ -30,9 +30,9 @@ } void InstanceID::GetEncryptionInfo(const std::string& authorized_entity, - const GetEncryptionInfoCallback& callback) { + GetEncryptionInfoCallback callback) { gcm_driver_->GetEncryptionProviderInternal()->GetEncryptionInfo( - app_id_, authorized_entity, callback); + app_id_, authorized_entity, std::move(callback)); } void InstanceID::DeleteToken(const std::string& authorized_entity,
diff --git a/components/gcm_driver/instance_id/instance_id.h b/components/gcm_driver/instance_id/instance_id.h index 71ac3a1..7b61d13b 100644 --- a/components/gcm_driver/instance_id/instance_id.h +++ b/components/gcm_driver/instance_id/instance_id.h
@@ -60,7 +60,7 @@ base::OnceCallback<void(const std::string& token, Result result)>; using ValidateTokenCallback = base::Callback<void(bool is_valid)>; using GetEncryptionInfoCallback = - base::Callback<void(const std::string&, const std::string&)>; + base::OnceCallback<void(std::string p256dh, std::string auth_secret)>; using DeleteTokenCallback = base::OnceCallback<void(Result result)>; using DeleteIDCallback = base::OnceCallback<void(Result result)>; @@ -117,7 +117,7 @@ // |authorized_entity|: the authorized entity passed when obtaining the token. // |callback|: to be called once the asynchronous operation is done. void GetEncryptionInfo(const std::string& authorized_entity, - const GetEncryptionInfoCallback& callback); + GetEncryptionInfoCallback callback); // Revokes a granted token. // |authorized_entity|: the authorized entity passed when obtaining the token.
diff --git a/components/google/core/browser/google_util_unittest.cc b/components/google/core/browser/google_util_unittest.cc index fa41784..a51544a 100644 --- a/components/google/core/browser/google_util_unittest.cc +++ b/components/google/core/browser/google_util_unittest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "components/google/core/common/google_util.h" + #include "base/command_line.h" #include "base/macros.h" #include "base/strings/stringprintf.h" @@ -12,19 +13,15 @@ using google_util::IsGoogleDomainUrl; - // Helpers -------------------------------------------------------------------- namespace { -const std::string kValidSearchSchemes[] = { - "http", - "https" -}; +const std::string kValidSearchSchemes[] = {"http", "https"}; const std::string kValidSearchQueryParams[] = { - "q", - "as_q" // Advanced search uses "as_q" instead of "q" as the query param. + "q", + "as_q" // Advanced search uses "as_q" instead of "q" as the query param. }; // These functions merely provide brevity in the callers. @@ -43,7 +40,6 @@ } // namespace - // Actual tests --------------------------------------------------------------- TEST(GoogleUtilTest, GoodHomePagesNonSecure) { @@ -174,7 +170,8 @@ "%s://ipv6.google.com?name=bob#age=24&%s=something", // Trailing dots in the hosts. - "%s://www.google.com./#%s=something", "%s://www.google.de./#%s=something", + "%s://www.google.com./#%s=something", + "%s://www.google.de./#%s=something", "%s://ipv4.google.com./#%s=something", "%s://ipv6.google.com./#%s=something", }; @@ -182,8 +179,7 @@ for (const std::string& pattern : patterns) { for (const std::string& scheme : kValidSearchSchemes) { for (const std::string& query_param : kValidSearchQueryParams) { - EXPECT_TRUE(IsSearch(base::StringPrintf(pattern.c_str(), - scheme.c_str(), + EXPECT_TRUE(IsSearch(base::StringPrintf(pattern.c_str(), scheme.c_str(), query_param.c_str()))); } } @@ -203,45 +199,39 @@ // Empty URL is invalid. EXPECT_FALSE(IsSearch(std::string())); - const std::string patterns[] = { - "%s://google.com", - "%s://www.google.com", - "%s://www.google.com/search", - "%s://www.google.com/search?" - }; + const std::string patterns[] = {"%s://google.com", "%s://www.google.com", + "%s://www.google.com/search", + "%s://www.google.com/search?"}; for (const std::string& pattern : patterns) { for (const std::string& scheme : kValidSearchSchemes) { - EXPECT_FALSE(IsSearch(base::StringPrintf(pattern.c_str(), - scheme.c_str()))); + EXPECT_FALSE( + IsSearch(base::StringPrintf(pattern.c_str(), scheme.c_str()))); } } const std::string patterns_q[] = { - // Home page searches without a hash fragment query parameter are invalid. - "%s://www.google.com/webhp?%s=something", - "%s://www.google.com/webhp?%s=something#no=good", - "%s://www.google.com/webhp?name=bob&%s=something", - "%s://www.google.com/?%s=something", - "%s://www.google.com?%s=something", + // Home page searches without a hash fragment query parameter are invalid. + "%s://www.google.com/webhp?%s=something", + "%s://www.google.com/webhp?%s=something#no=good", + "%s://www.google.com/webhp?name=bob&%s=something", + "%s://www.google.com/?%s=something", "%s://www.google.com?%s=something", - // Some paths are outright invalid as searches. - "%s://www.google.com/notreal?%s=something", - "%s://www.google.com/chrome?%s=something", - "%s://www.google.com/search/nogood?%s=something", - "%s://www.google.com/webhp/nogood#%s=something", + // Some paths are outright invalid as searches. + "%s://www.google.com/notreal?%s=something", + "%s://www.google.com/chrome?%s=something", + "%s://www.google.com/search/nogood?%s=something", + "%s://www.google.com/webhp/nogood#%s=something", - // Case sensitive paths. - "%s://www.google.com/SEARCH?%s=something", - "%s://www.google.com/WEBHP#%s=something" - }; + // Case sensitive paths. + "%s://www.google.com/SEARCH?%s=something", + "%s://www.google.com/WEBHP#%s=something"}; for (const std::string& pattern : patterns_q) { for (const std::string& scheme : kValidSearchSchemes) { for (const std::string& query_param : kValidSearchQueryParams) { - EXPECT_FALSE(IsSearch(base::StringPrintf(pattern.c_str(), - scheme.c_str(), - query_param.c_str()))); + EXPECT_FALSE(IsSearch(base::StringPrintf( + pattern.c_str(), scheme.c_str(), query_param.c_str()))); } } } @@ -341,8 +331,8 @@ // By default, none of the IsGoogleXXX functions should return true for a // "foo.com" URL. - EXPECT_FALSE(IsGoogleHostname("www.foo.com", - google_util::DISALLOW_SUBDOMAIN)); + EXPECT_FALSE( + IsGoogleHostname("www.foo.com", google_util::DISALLOW_SUBDOMAIN)); EXPECT_FALSE(IsGoogleDomainUrl(GURL("http://www.foo.com/xyz"), google_util::DISALLOW_SUBDOMAIN, google_util::DISALLOW_NON_STANDARD_PORTS)); @@ -409,8 +399,8 @@ google_util::ALLOW_SUBDOMAIN, google_util::DISALLOW_NON_STANDARD_PORTS)); EXPECT_FALSE(IsYoutubeDomainUrl(GURL("http://notyoutube.com"), - google_util::ALLOW_SUBDOMAIN, - google_util::DISALLOW_NON_STANDARD_PORTS)); + google_util::ALLOW_SUBDOMAIN, + google_util::DISALLOW_NON_STANDARD_PORTS)); // TLD checks. EXPECT_TRUE(IsYoutubeDomainUrl(GURL("http://www.youtube.ca"),
diff --git a/components/language/content/browser/geo_language_provider.cc b/components/language/content/browser/geo_language_provider.cc index fd647ef..a2cde93e 100644 --- a/components/language/content/browser/geo_language_provider.cc +++ b/components/language/content/browser/geo_language_provider.cc
@@ -153,14 +153,14 @@ device::mojom::GeopositionPtr geoposition) { DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_); - const std::vector<std::string> languages = - language_code_locator_->GetLanguageCodes(geoposition->latitude, - geoposition->longitude); - - // Update current languages on UI thread. + // Update current languages on UI thread. We pass the lat/long pair so that + // SetGeoLanguages can do the lookup on the UI thread. This is because the + // language provider could decide to cache the values, requiring interaction + // with the pref service. creation_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GeoLanguageProvider::SetGeoLanguages, - base::Unretained(this), languages)); + FROM_HERE, base::BindOnce(&GeoLanguageProvider::LookupAndSetLanguages, + base::Unretained(this), geoposition->latitude, + geoposition->longitude)); // Post a task to request a fresh lookup after |kMinUpdatePeriod|. background_task_runner_->PostDelayedTask( @@ -170,10 +170,20 @@ kMinUpdatePeriod); } +void GeoLanguageProvider::LookupAndSetLanguages(double lat, double lon) { + DCHECK_CALLED_ON_VALID_SEQUENCE(creation_sequence_checker_); + // Perform the lookup here (as opposed to the geolocation callback), as the + // locator could cache the value in a pref, which must happen on the UI thread + // This behavior is factored out in this function in order for tests to be + // able to call SetGeoLanguages directly. + SetGeoLanguages(language_code_locator_->GetLanguageCodes(lat, lon)); +} + void GeoLanguageProvider::SetGeoLanguages( const std::vector<std::string>& languages) { DCHECK_CALLED_ON_VALID_SEQUENCE(creation_sequence_checker_); languages_ = languages; + base::ListValue cache_list; for (size_t i = 0; i < languages_.size(); ++i) { cache_list.Set(i, std::make_unique<base::Value>(languages_[i]));
diff --git a/components/language/content/browser/geo_language_provider.h b/components/language/content/browser/geo_language_provider.h index 6018029..2166e0d 100644 --- a/components/language/content/browser/geo_language_provider.h +++ b/components/language/content/browser/geo_language_provider.h
@@ -82,6 +82,10 @@ // necessary. void QueryNextPosition(); + // Lookup the languages from the lat/lon pair, and pass them to + // SetGeoLanguages. Must be called on the UI thread. + void LookupAndSetLanguages(double lat, double lon); + // Updates the list of BCP-47 language codes that will be returned by calls to // CurrentGeoLanguages(). // Must be called on the UI thread.
diff --git a/components/optimization_guide/BUILD.gn b/components/optimization_guide/BUILD.gn index b405cb4..7e36d19 100644 --- a/components/optimization_guide/BUILD.gn +++ b/components/optimization_guide/BUILD.gn
@@ -7,6 +7,8 @@ "hints_component_info.h", "hints_component_util.cc", "hints_component_util.h", + "hints_processing_util.cc", + "hints_processing_util.h", "optimization_guide_constants.cc", "optimization_guide_constants.h", "optimization_guide_features.cc", @@ -28,6 +30,7 @@ "//components/prefs", "//google_apis", "//net:net", + "//url:url", ] } @@ -49,6 +52,7 @@ testonly = true sources = [ "hints_component_util_unittest.cc", + "hints_processing_util_unittest.cc", "optimization_guide_features_unittest.cc", "optimization_guide_service_unittest.cc", "url_pattern_with_wildcards_unittest.cc", @@ -60,5 +64,6 @@ "//base/test:test_support", "//components/optimization_guide/proto:optimization_guide_proto", "//testing/gtest", + "//url:url", ] }
diff --git a/components/previews/content/previews_hints_util.cc b/components/optimization_guide/hints_processing_util.cc similarity index 66% rename from components/previews/content/previews_hints_util.cc rename to components/optimization_guide/hints_processing_util.cc index fd5dd25..bf83d5e5 100644 --- a/components/previews/content/previews_hints_util.cc +++ b/components/optimization_guide/hints_processing_util.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/previews/content/previews_hints_util.h" +#include "components/optimization_guide/hints_processing_util.h" #include <string> @@ -13,18 +13,17 @@ #include "components/optimization_guide/url_pattern_with_wildcards.h" #include "url/gurl.h" -namespace previews { +namespace optimization_guide { bool IsDisabledPerOptimizationHintExperiment( - const optimization_guide::proto::Optimization& optimization) { + const proto::Optimization& optimization) { // First check if optimization depends on an experiment being enabled. if (optimization.has_experiment_name() && !optimization.experiment_name().empty() && optimization.experiment_name() != base::GetFieldTrialParamValueByFeature( - optimization_guide::features::kOptimizationHintsExperiments, - optimization_guide::features:: - kOptimizationHintsExperimentNameParam)) { + features::kOptimizationHintsExperiments, + features::kOptimizationHintsExperimentNameParam)) { return true; } // Now check if optimization depends on an experiment not being enabled. @@ -32,17 +31,15 @@ !optimization.excluded_experiment_name().empty() && optimization.excluded_experiment_name() == base::GetFieldTrialParamValueByFeature( - optimization_guide::features::kOptimizationHintsExperiments, - optimization_guide::features:: - kOptimizationHintsExperimentNameParam)) { + features::kOptimizationHintsExperiments, + features::kOptimizationHintsExperimentNameParam)) { return true; } return false; } -const optimization_guide::proto::PageHint* FindPageHintForURL( - const GURL& gurl, - const optimization_guide::proto::Hint* hint) { +const proto::PageHint* FindPageHintForURL(const GURL& gurl, + const proto::Hint* hint) { if (!hint) { return nullptr; } @@ -51,8 +48,7 @@ if (page_hint.page_pattern().empty()) { continue; } - optimization_guide::URLPatternWithWildcards url_pattern( - page_hint.page_pattern()); + URLPatternWithWildcards url_pattern(page_hint.page_pattern()); if (url_pattern.Matches(gurl.spec())) { // Return the first matching page hint. return &page_hint; @@ -65,4 +61,4 @@ return base::StringPrintf("%x", base::PersistentHash(host)); } -} // namespace previews +} // namespace optimization_guide
diff --git a/components/previews/content/previews_hints_util.h b/components/optimization_guide/hints_processing_util.h similarity index 76% rename from components/previews/content/previews_hints_util.h rename to components/optimization_guide/hints_processing_util.h index ce71f1a..69ae400 100644 --- a/components/previews/content/previews_hints_util.h +++ b/components/optimization_guide/hints_processing_util.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 COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_ -#define COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_ +#ifndef COMPONENTS_OPTIMIZATION_GUIDE_HINTS_PROCESSING_UTIL_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_HINTS_PROCESSING_UTIL_H_ #include <string> @@ -14,9 +14,6 @@ class Optimization; class PageHint; } // namespace proto -} // namespace optimization_guide - -namespace previews { // Returns whether |optimization| is disabled subject to it being part of // an optimization hint experiment. |optimization| could be disabled either @@ -25,12 +22,11 @@ // may be configured for the client with the experiment_name parameter to the // kOptimizationHintsExperiments feature. bool IsDisabledPerOptimizationHintExperiment( - const optimization_guide::proto::Optimization& optimization); + const proto::Optimization& optimization); // Returns the matching PageHint for |gurl| if found in |hint|. -const optimization_guide::proto::PageHint* FindPageHintForURL( - const GURL& gurl, - const optimization_guide::proto::Hint* hint); +const proto::PageHint* FindPageHintForURL(const GURL& gurl, + const proto::Hint* hint); // The host is hashed and returned as a string because base::DictionaryValue // only accepts strings as keys. Note, some hash collisions could occur on @@ -41,6 +37,6 @@ // practically zero. std::string HashHostForDictionary(const std::string& host); -} // namespace previews +} // namespace optimization_guide -#endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_UTIL_H_ +#endif // COMPONENTS_OPTIMIZATION_GUIDE_HINTS_PROCESSING_UTIL_H_
diff --git a/components/previews/content/previews_hints_util_unittest.cc b/components/optimization_guide/hints_processing_util_unittest.cc similarity index 76% rename from components/previews/content/previews_hints_util_unittest.cc rename to components/optimization_guide/hints_processing_util_unittest.cc index b3e02c25..6ed6617 100644 --- a/components/previews/content/previews_hints_util_unittest.cc +++ b/components/optimization_guide/hints_processing_util_unittest.cc
@@ -2,33 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "components/previews/content/previews_hints_util.h" +#include "components/optimization_guide/hints_processing_util.h" #include "components/optimization_guide/proto/hints.pb.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" -namespace previews { +namespace optimization_guide { -class PreviewsHintsUtilTest : public testing::Test { +class HintsProcessingUtilTest : public testing::Test { public: - PreviewsHintsUtilTest() {} - ~PreviewsHintsUtilTest() override {} + HintsProcessingUtilTest() {} + ~HintsProcessingUtilTest() override {} }; -TEST_F(PreviewsHintsUtilTest, FindPageHintForSubstringPagePattern) { - optimization_guide::proto::Hint hint1; +TEST_F(HintsProcessingUtilTest, FindPageHintForSubstringPagePattern) { + proto::Hint hint1; // Page hint for "/one/" - optimization_guide::proto::PageHint* page_hint1 = hint1.add_page_hints(); + proto::PageHint* page_hint1 = hint1.add_page_hints(); page_hint1->set_page_pattern("foo.org/*/one/"); // Page hint for "two" - optimization_guide::proto::PageHint* page_hint2 = hint1.add_page_hints(); + proto::PageHint* page_hint2 = hint1.add_page_hints(); page_hint2->set_page_pattern("two"); // Page hint for "three.jpg" - optimization_guide::proto::PageHint* page_hint3 = hint1.add_page_hints(); + proto::PageHint* page_hint3 = hint1.add_page_hints(); page_hint3->set_page_pattern("three.jpg"); EXPECT_EQ(nullptr, FindPageHintForURL(GURL(""), &hint1)); @@ -62,4 +62,4 @@ GURL("https://www.foo.org/bar/three.jpg"), &hint1)); } -} // namespace previews +} // namespace optimization_guide
diff --git a/components/password_manager/core/browser/password_generation_state.cc b/components/password_manager/core/browser/password_generation_state.cc index 9a88a0574..0b691ec 100644 --- a/components/password_manager/core/browser/password_generation_state.cc +++ b/components/password_manager/core/browser/password_generation_state.cc
@@ -20,6 +20,8 @@ namespace { using autofill::PasswordForm; +using metrics_util::GenerationPresaveConflict; +using metrics_util::LogGenerationPresaveConflict; std::vector<PasswordForm> DeepCopyVector( const std::vector<const PasswordForm*>& forms) { @@ -137,6 +139,7 @@ } void PasswordDataForUI::Save() { + LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_SAVE); bubble_interaction_cb_.Run(true, pending_form_); } @@ -155,14 +158,17 @@ } void PasswordDataForUI::OnNopeUpdateClicked() { + LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_CANCEL); bubble_interaction_cb_.Run(false, pending_form_); } void PasswordDataForUI::OnNeverClicked() { + LogPresavedUpdateUIDismissalReason(metrics_util::CLICKED_NEVER); bubble_interaction_cb_.Run(false, pending_form_); } void PasswordDataForUI::OnNoInteraction(bool is_update) { + LogPresavedUpdateUIDismissalReason(metrics_util::NO_DIRECT_INTERACTION); bubble_interaction_cb_.Run(false, pending_form_); } @@ -214,6 +220,8 @@ generated.username_value.clear(); const PasswordForm* conflict = FindUsernameConflict(generated, matches); if (conflict) { + LogGenerationPresaveConflict( + GenerationPresaveConflict::kConflictWithEmptyUsername); auto bubble_launcher = std::make_unique<PasswordDataForUI>( std::move(generated), matches, fetcher.GetFederatedMatches(), base::BindRepeating(&PasswordGenerationState::OnPresaveBubbleResult, @@ -221,7 +229,13 @@ client_->PromptUserToSaveOrUpdatePassword(std::move(bubble_launcher), true); return; + } else { + LogGenerationPresaveConflict( + GenerationPresaveConflict::kNoConflictWithEmptyUsername); } + } else { + LogGenerationPresaveConflict( + GenerationPresaveConflict::kNoUsernameConflict); } driver->GeneratedPasswordAccepted(generated.password_value); }
diff --git a/components/password_manager/core/browser/password_generation_state_unittest.cc b/components/password_manager/core/browser/password_generation_state_unittest.cc index cd8ad1f1..dd47e532 100644 --- a/components/password_manager/core/browser/password_generation_state_unittest.cc +++ b/components/password_manager/core/browser/password_generation_state_unittest.cc
@@ -6,6 +6,7 @@ #include "base/memory/scoped_refptr.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" #include "base/test/simple_test_clock.h" @@ -175,6 +176,9 @@ // Check that accepting a generated password simply relays the message to the // driver. TEST_F(PasswordGenerationStateTest, GeneratedPasswordAccepted_EmptyStore) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kGenerationNoOverwrites); PasswordForm generated = CreateGenerated(); MockPasswordManagerDriver driver; FakeFormFetcher fetcher; @@ -183,12 +187,18 @@ state().GeneratedPasswordAccepted(std::move(generated), fetcher, driver.AsWeakPtr()); EXPECT_FALSE(state().HasGeneratedPassword()); + histogram_tester.ExpectUniqueSample( + "PasswordGeneration.PresaveConflict", + metrics_util::GenerationPresaveConflict::kNoUsernameConflict, 1); } // In case of accepted password conflicts with an existing username the // credential can be presaved with an empty one. Thus, no conflict happens and // the driver should be notified directly. TEST_F(PasswordGenerationStateTest, GeneratedPasswordAccepted_Conflict) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kGenerationNoOverwrites); PasswordForm generated = CreateGenerated(); const PasswordForm saved = CreateSaved(); generated.username_value = saved.username_value; @@ -200,9 +210,13 @@ state().GeneratedPasswordAccepted(std::move(generated), fetcher, driver.AsWeakPtr()); EXPECT_FALSE(state().HasGeneratedPassword()); + histogram_tester.ExpectUniqueSample( + "PasswordGeneration.PresaveConflict", + metrics_util::GenerationPresaveConflict::kNoConflictWithEmptyUsername, 1); } TEST_F(PasswordGenerationStateTest, GeneratedPasswordAccepted_UpdateUI) { + base::HistogramTester histogram_tester; base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kGenerationNoOverwrites); MockPasswordManagerDriver driver; @@ -220,6 +234,9 @@ EXPECT_THAT(ui_form->GetBlacklistedMatches(), IsEmpty()); EXPECT_THAT(ui_form->GetInteractionsStats(), IsEmpty()); EXPECT_FALSE(ui_form->IsBlacklisted()); + histogram_tester.ExpectUniqueSample( + "PasswordGeneration.PresaveConflict", + metrics_util::GenerationPresaveConflict::kConflictWithEmptyUsername, 1); } TEST_F(PasswordGenerationStateTest,
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc index 97ee12d6..28556a7 100644 --- a/components/password_manager/core/browser/password_manager_metrics_util.cc +++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -47,6 +47,12 @@ reason, NUM_UI_RESPONSES); } +void LogPresavedUpdateUIDismissalReason(UIDismissalReason reason) { + base::UmaHistogramEnumeration( + "PasswordManager.PresavedUpdateUIDismissalReason", reason, + NUM_UI_RESPONSES); +} + void LogUIDisplayDisposition(UIDisplayDisposition disposition) { base::UmaHistogramEnumeration("PasswordBubble.DisplayDisposition", disposition, NUM_DISPLAY_DISPOSITIONS); @@ -215,6 +221,10 @@ value); } +void LogGenerationPresaveConflict(GenerationPresaveConflict value) { + base::UmaHistogramEnumeration("PasswordGeneration.PresaveConflict", value); +} + #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) void LogSyncPasswordHashChange(SyncPasswordHashChange event) { base::UmaHistogramEnumeration(
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h index b4dbf4b3..a895f9d 100644 --- a/components/password_manager/core/browser/password_manager_metrics_util.h +++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -334,6 +334,18 @@ kMaxValue = kGenerate }; +// Type of the conflict with existing credentials when starting password +// generation. +enum class GenerationPresaveConflict { + // Credential can be presaved as is. + kNoUsernameConflict = 0, + // Credential can be presaved without username. + kNoConflictWithEmptyUsername = 1, + // Credential should overwrite one without username. + kConflictWithEmptyUsername = 2, + kMaxValue = kConflictWithEmptyUsername +}; + // A version of the UMA_HISTOGRAM_BOOLEAN macro that allows the |name| // to vary over the program's runtime. void LogUMAHistogramBoolean(const std::string& name, bool sample); @@ -348,6 +360,10 @@ // Log the |reason| a user dismissed the update password bubble. void LogUpdateUIDismissalReason(UIDismissalReason reason); +// Log the |reason| a user dismissed the update password bubble when resolving a +// conflict during generation. +void LogPresavedUpdateUIDismissalReason(UIDismissalReason reason); + // Log the appropriate display disposition. void LogUIDisplayDisposition(UIDisplayDisposition disposition); @@ -436,6 +452,10 @@ // Log whether a saved password was generated. void LogNewlySavedPasswordIsGenerated(bool value); +// Log whether there is a conflict with existing credentials when presaving +// a generated password. +void LogGenerationPresaveConflict(GenerationPresaveConflict value); + #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED) // Log a save sync password change event. void LogSyncPasswordHashChange(SyncPasswordHashChange event);
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc index 2f26fdb..ab0c2331 100644 --- a/components/password_manager/core/common/password_manager_features.cc +++ b/components/password_manager/core/common/password_manager_features.cc
@@ -59,6 +59,10 @@ const base::Feature kOnlyNewParser = {"only-new-password-form-parsing", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables editing saved passwords for Android. +const base::Feature kPasswordEditingAndroid = { + "PasswordEditingAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls the ability to import passwords from Chrome's settings page. const base::Feature kPasswordImport = {"PasswordImport", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h index bdaae11..4490c345 100644 --- a/components/password_manager/core/common/password_manager_features.h +++ b/components/password_manager/core/common/password_manager_features.h
@@ -27,6 +27,7 @@ extern const base::Feature kNewPasswordFormParsing; extern const base::Feature kNewPasswordFormParsingForSaving; extern const base::Feature kOnlyNewParser; +extern const base::Feature kPasswordEditingAndroid; extern const base::Feature kPasswordImport; extern const base::Feature kRecoverPasswordsForSyncUsers; extern const base::Feature kTouchToFillAndroid;
diff --git a/components/payments/content/payment_event_response_util.cc b/components/payments/content/payment_event_response_util.cc index b7de46a..60adab8 100644 --- a/components/payments/content/payment_event_response_util.cc +++ b/components/payments/content/payment_event_response_util.cc
@@ -6,6 +6,7 @@ #include "base/strings/string_piece.h" #include "components/payments/core/error_strings.h" +#include "components/payments/core/native_error_strings.h" namespace payments {
diff --git a/components/payments/content/payment_handler_host.cc b/components/payments/content/payment_handler_host.cc index ca4deb7e..6e45a838 100644 --- a/components/payments/content/payment_handler_host.cc +++ b/components/payments/content/payment_handler_host.cc
@@ -8,6 +8,7 @@ #include "base/callback.h" #include "components/payments/core/error_strings.h" +#include "components/payments/core/native_error_strings.h" #include "content/public/browser/browser_thread.h" namespace payments {
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc index 5c46593..1771e48 100644 --- a/components/payments/content/payment_request.cc +++ b/components/payments/content/payment_request.cc
@@ -21,6 +21,7 @@ #include "components/payments/core/can_make_payment_query.h" #include "components/payments/core/error_strings.h" #include "components/payments/core/features.h" +#include "components/payments/core/native_error_strings.h" #include "components/payments/core/payment_details.h" #include "components/payments/core/payment_details_validation.h" #include "components/payments/core/payment_instrument.h"
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn index 9a17ca0..9688cb5 100644 --- a/components/payments/core/BUILD.gn +++ b/components/payments/core/BUILD.gn
@@ -98,6 +98,8 @@ sources = [ "error_strings.cc", "error_strings.h", + "native_error_strings.cc", + "native_error_strings.h", ] }
diff --git a/components/payments/core/error_strings.cc b/components/payments/core/error_strings.cc index b228932..70b9b5d 100644 --- a/components/payments/core/error_strings.cc +++ b/components/payments/core/error_strings.cc
@@ -12,43 +12,16 @@ const char kAnotherUiShowing[] = "Another PaymentRequest UI is already showing in a different tab or window."; const char kAttemptedInitializationTwice[] = "Attempted initialization twice."; -const char kCannotAbortWithoutInit[] = "Attempted abort without initialization."; -const char kCannotAbortWithoutShow[] = "Attempted abort without show."; -const char kCannotCallCanMakePaymentWithoutInit[] = "Attempted canMakePayment without initialization."; -const char kCannotCallHasEnrolledInstrumentWithoutInit[] = "Attempted hasEnrolledInstrument without initialization."; -const char kCannotCompleteWithoutInit[] = "Attempted complete without initialization."; -const char kCannotCompleteWithoutShow[] = "Attempted complete without show."; -const char kCannotRetryWithoutInit[] = "Attempted retry without initialization."; -const char kCannotRetryWithoutShow[] = "Attempted retry without show."; const char kCannotShowInBackgroundTab[] = "Cannot show PaymentRequest UI in a background tab."; const char kCannotShowTwice[] = "Attempted show twice."; const char kCannotShowWithoutInit[] = "Attempted show without initialization."; const char kCannotUpdateWithoutInit[] = "Attempted updateWith without initialization."; const char kCannotUpdateWithoutShow[] = "Attempted updateWith without show."; -const char kDetailedInvalidSslCertificateMessageFormat[] = "SSL certificate is not valid. Security level: $."; const char kGenericPaymentMethodNotSupportedMessage[] = "Payment method not supported."; -const char kInvalidSslCertificate[] = "SSL certificate is not valid."; const char kInvalidState[] = "Invalid state."; -const char kMethodDataRequired[] = "Method data required."; -const char kMethodNameRequired[] = "Method name required."; -const char kMissingDetailsFromPaymentApp[] = "Payment app returned invalid response. Missing field \"details\"."; -const char kMissingMethodNameFromPaymentApp[] = "Payment app returned invalid response. Missing field \"methodName\"."; -const char kMultiplePaymentMethodsNotSupportedFormat[] = "The payment methods $ are not supported."; -const char kNoResponseToPaymentEvent[] = "Payment handler did not respond to \"paymentrequest\" event."; const char kNotInASecureOrigin[] = "Not in a secure origin."; -const char kNotInitialized[] = "Not initialized."; -const char kNotShown[] = "Not shown."; -const char kPaymentDetailsNotObject[] = "Payment app returned invalid response. \"details\" field is not a dictionary."; -const char kPaymentDetailsStringifyError[] = "Payment app returned invalid response. Unable to JSON-serialize \"details\"."; -const char kPaymentEventBrowserError[] = "Browser encountered an error when firing the \"paymentrequest\" event in the payment handler."; -const char kPaymentEventInternalError[] = "Payment handler encountered an internal error when handling the \"paymentrequest\" event."; -const char kPaymentEventRejected[] = "Payment handler rejected the promise passed into PaymentRequestEvent.respondWith(). This is how payment handlers close their own window programmatically."; -const char kPaymentEventServiceWorkerError[] = "Payment handler failed to provide a response because either the \"paymentrequest\" event took too long or the service worker stopped for some reason or was killed before the request finished."; -const char kPaymentEventTimeout[] = "The \"paymentrequest\" event timed out after 5 minutes."; -const char kPaymentHandlerInsecureNavigation[] = "The payment handler navigated to a page with insecure context, invalid certificate state, or malicious content."; const char kProhibitedOrigin[] = "Only localhost, file://, and cryptographic scheme origins allowed."; const char kProhibitedOriginOrInvalidSslExplanation[] = "No UI will be shown. CanMakePayment and hasEnrolledInstrument will always return false. Show will be rejected with NotSupportedError."; -const char kSinglePaymentMethodNotSupportedFormat[] = "The payment method $ is not supported."; const char kTotalRequired[] = "Total required."; const char kUserCancelled[] = "User closed the Payment Request UI.";
diff --git a/components/payments/core/error_strings.h b/components/payments/core/error_strings.h index a60c2558..33293dd3 100644 --- a/components/payments/core/error_strings.h +++ b/components/payments/core/error_strings.h
@@ -8,6 +8,9 @@ namespace payments { namespace errors { +// These strings are referenced from both C++ and Java (through the +// auto-generated file ErrorStrings.java). + // Please keep the list alphabetized. // Only a single PaymentRequest UI can be displayed at a time. @@ -16,32 +19,6 @@ // Mojo call PaymentRequest::Init() must precede PaymentRequest::Show(). extern const char kAttemptedInitializationTwice[]; -// Mojo call PaymentRequest::Init() must precede PaymentRequest::Abort(). -extern const char kCannotAbortWithoutInit[]; - -// Mojo call PaymentRequest::Show() must precede PaymentRequest::Abort(). -extern const char kCannotAbortWithoutShow[]; - -// Mojo call PaymentRequest::Init() must precede -// PaymentRequest::CanMakePayment(). -extern const char kCannotCallCanMakePaymentWithoutInit[]; - -// Mojo call PaymentRequest::Init() must precede -// PaymentRequest::HasEnrolledInstrument(). -extern const char kCannotCallHasEnrolledInstrumentWithoutInit[]; - -// Mojo call PaymentRequest::Init() must precede PaymentRequest::Complete(). -extern const char kCannotCompleteWithoutInit[]; - -// Mojo call PaymentRequest::Show() must precede PaymentRequest::Complete(). -extern const char kCannotCompleteWithoutShow[]; - -// Mojo call PaymentRequest::Init() must precede PaymentRequest::Retry(). -extern const char kCannotRetryWithoutInit[]; - -// Mojo call PaymentRequest::Show() must precede PaymentRequest::Retry(). -extern const char kCannotRetryWithoutShow[]; - // Payment Request UI must be shown in the foreground tab, as a result of user // interaction. extern const char kCannotShowInBackgroundTab[]; @@ -58,82 +35,15 @@ // Mojo call PaymentRequest::Show() must precede PaymentRequest::UpdateWith(). extern const char kCannotUpdateWithoutShow[]; -// The format for a detailed message about invalid SSL certificate. This format -// should be used with base::ReplaceChars() function, where "$" is the character -// to replace. -extern const char kDetailedInvalidSslCertificateMessageFormat[]; - // A message about unsupported payment method. extern const char kGenericPaymentMethodNotSupportedMessage[]; -// Chrome refuses to provide any payment information to a website with an -// invalid SSL certificate. -extern const char kInvalidSslCertificate[]; - // Used when an invalid state is encountered generically. extern const char kInvalidState[]; -// Used when the {"supportedMethods": "", data: {}} is required, but not -// provided. -extern const char kMethodDataRequired[]; - -// Used when non-empty "supportedMethods": "" is required, but not provided. -extern const char kMethodNameRequired[]; - -// The payment handler responded with an empty "details" field. -extern const char kMissingDetailsFromPaymentApp[]; - -// The payment handler responded with an empty "methodName" field. -extern const char kMissingMethodNameFromPaymentApp[]; - -// The format for the message about multiple payment methods that are not -// supported. This format should be used with base::ReplaceChars() function, -// where "$" is the character to replace. -extern const char kMultiplePaymentMethodsNotSupportedFormat[]; - -// Payment handler did not respond to the "paymentrequest" event. -extern const char kNoResponseToPaymentEvent[]; - // The PaymentRequest API is available only on secure origins. extern const char kNotInASecureOrigin[]; -// Used when PaymentRequest::Init() has not been called, but should have been. -extern const char kNotInitialized[]; - -// Used when PaymentRequest::Show() has not been called, but should have been. -extern const char kNotShown[]; - -// Payment handler passed a non-object field "details" in response to the -// "paymentrequest" event. -extern const char kPaymentDetailsNotObject[]; - -// Payment handler passed a non-stringifiable field "details" in response to the -// "paymentrequest" event. -extern const char kPaymentDetailsStringifyError[]; - -// Used when the browser failed to fire the "paymentrequest" event without any -// actionable corrective action from the web developer. -extern const char kPaymentEventBrowserError[]; - -// Service worker timed out or stopped for some reason or was killed before the -// payment handler could respond to the "paymentrequest" event. -extern const char kPaymentEventServiceWorkerError[]; - -// Service worker timed out while responding to "paymentrequest" event. -extern const char kPaymentEventTimeout[]; - -// Payment handler navigated to a page with insecure context, invalid SSL, or -// malicious content. -extern const char kPaymentHandlerInsecureNavigation[]; - -// Payment handler encountered an internal error when handling the -// "paymentrequest" event. -extern const char kPaymentEventInternalError[]; - -// Payment handler rejected the promise passed into -// PaymentRequestEvent.respondWith() method. -extern const char kPaymentEventRejected[]; - // Chrome provides payment information only to a whitelist of origin types. extern const char kProhibitedOrigin[]; @@ -141,11 +51,6 @@ // or kInvalidSslCertificate error. extern const char kProhibitedOriginOrInvalidSslExplanation[]; -// The format for the message about a single payment method that is not -// supported. This format should be used with base::ReplaceChars() function, -// where "$" is the character to replace. -extern const char kSinglePaymentMethodNotSupportedFormat[]; - // Used when "total": {"label": "Total", "amount": {"currency": "USD", "value": // "0.01"}} is required, bot not provided. extern const char kTotalRequired[];
diff --git a/components/payments/core/native_error_strings.cc b/components/payments/core/native_error_strings.cc new file mode 100644 index 0000000..d7c8221 --- /dev/null +++ b/components/payments/core/native_error_strings.cc
@@ -0,0 +1,93 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/payments/core/native_error_strings.h" + +namespace payments { +namespace errors { + +const char kCannotAbortWithoutInit[] = + "Attempted abort without initialization."; + +const char kCannotAbortWithoutShow[] = "Attempted abort without show."; + +const char kCannotCallCanMakePaymentWithoutInit[] = + "Attempted canMakePayment without initialization."; + +const char kCannotCallHasEnrolledInstrumentWithoutInit[] = + "Attempted hasEnrolledInstrument without initialization."; + +const char kCannotCompleteWithoutInit[] = + "Attempted complete without initialization."; + +const char kCannotCompleteWithoutShow[] = "Attempted complete without show."; + +const char kCannotRetryWithoutInit[] = + "Attempted retry without initialization."; + +const char kCannotRetryWithoutShow[] = "Attempted retry without show."; + +const char kDetailedInvalidSslCertificateMessageFormat[] = + "SSL certificate is not valid. Security level: $."; + +const char kInvalidSslCertificate[] = "SSL certificate is not valid."; + +const char kMethodDataRequired[] = "Method data required."; + +const char kMethodNameRequired[] = "Method name required."; + +const char kMissingDetailsFromPaymentApp[] = + "Payment app returned invalid response. Missing field \"details\"."; + +const char kMissingMethodNameFromPaymentApp[] = + "Payment app returned invalid response. Missing field \"methodName\"."; + +const char kMultiplePaymentMethodsNotSupportedFormat[] = + "The payment methods $ are not supported."; + +const char kNoResponseToPaymentEvent[] = + "Payment handler did not respond to \"paymentrequest\" event."; + +const char kNotInitialized[] = "Not initialized."; + +const char kNotShown[] = "Not shown."; + +const char kPaymentDetailsNotObject[] = + "Payment app returned invalid response. \"details\" field is not a " + "dictionary."; + +const char kPaymentDetailsStringifyError[] = + "Payment app returned invalid response. Unable to JSON-serialize " + "\"details\"."; + +const char kPaymentEventBrowserError[] = + "Browser encountered an error when firing the \"paymentrequest\" event in " + "the payment handler."; + +const char kPaymentEventInternalError[] = + "Payment handler encountered an internal error when handling the " + "\"paymentrequest\" event."; + +const char kPaymentEventRejected[] = + "Payment handler rejected the promise passed into " + "PaymentRequestEvent.respondWith(). This is how payment handlers close " + "their own window programmatically."; + +const char kPaymentEventServiceWorkerError[] = + "Payment handler failed to provide a response because either the " + "\"paymentrequest\" event took too long or the service worker stopped for " + "some reason or was killed before the request finished."; + +const char kPaymentEventTimeout[] = + "The \"paymentrequest\" event timed out after 5 minutes."; + +const char kPaymentHandlerInsecureNavigation[] = + "The payment handler navigated to a page with insecure context, invalid " + "certificate state, or malicious content."; + +const char kSinglePaymentMethodNotSupportedFormat[] = + "The payment method $ is not supported."; + +} // namespace errors +} // namespace payments
diff --git a/components/payments/core/native_error_strings.h b/components/payments/core/native_error_strings.h new file mode 100644 index 0000000..927d146 --- /dev/null +++ b/components/payments/core/native_error_strings.h
@@ -0,0 +1,117 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PAYMENTS_CORE_NATIVE_ERROR_STRINGS_H_ +#define COMPONENTS_PAYMENTS_CORE_NATIVE_ERROR_STRINGS_H_ + +namespace payments { +namespace errors { + +// These strings are referenced only from C++. + +// Mojo call PaymentRequest::Init() must precede PaymentRequest::Abort(). +extern const char kCannotAbortWithoutInit[]; + +// Mojo call PaymentRequest::Show() must precede PaymentRequest::Abort(). +extern const char kCannotAbortWithoutShow[]; + +// Mojo call PaymentRequest::Init() must precede +// PaymentRequest::CanMakePayment(). +extern const char kCannotCallCanMakePaymentWithoutInit[]; + +// Mojo call PaymentRequest::Init() must precede +// PaymentRequest::HasEnrolledInstrument(). +extern const char kCannotCallHasEnrolledInstrumentWithoutInit[]; + +// Mojo call PaymentRequest::Init() must precede PaymentRequest::Complete(). +extern const char kCannotCompleteWithoutInit[]; + +// Mojo call PaymentRequest::Show() must precede PaymentRequest::Complete(). +extern const char kCannotCompleteWithoutShow[]; + +// Mojo call PaymentRequest::Init() must precede PaymentRequest::Retry(). +extern const char kCannotRetryWithoutInit[]; + +// Mojo call PaymentRequest::Show() must precede PaymentRequest::Retry(). +extern const char kCannotRetryWithoutShow[]; + +// Mojo call PaymentRequest::Show() must precede PaymentRequest::UpdateWith(). +extern const char kCannotUpdateWithoutShow[]; + +// The format for a detailed message about invalid SSL certificate. This format +// should be used with base::ReplaceChars() function, where "$" is the character +// to replace. +extern const char kDetailedInvalidSslCertificateMessageFormat[]; + +// Chrome refuses to provide any payment information to a website with an +// invalid SSL certificate. +extern const char kInvalidSslCertificate[]; + +// Used when the {"supportedMethods": "", data: {}} is required, but not +// provided. +extern const char kMethodDataRequired[]; + +// Used when non-empty "supportedMethods": "" is required, but not provided. +extern const char kMethodNameRequired[]; + +// The payment handler responded with an empty "details" field. +extern const char kMissingDetailsFromPaymentApp[]; + +// The payment handler responded with an empty "methodName" field. +extern const char kMissingMethodNameFromPaymentApp[]; + +// The format for the message about multiple payment methods that are not +// supported. This format should be used with base::ReplaceChars() function, +// where "$" is the character to replace. +extern const char kMultiplePaymentMethodsNotSupportedFormat[]; + +// Payment handler did not respond to the "paymentrequest" event. +extern const char kNoResponseToPaymentEvent[]; + +// Used when PaymentRequest::Init() has not been called, but should have been. +extern const char kNotInitialized[]; + +// Used when PaymentRequest::Show() has not been called, but should have been. +extern const char kNotShown[]; + +// Payment handler passed a non-object field "details" in response to the +// "paymentrequest" event. +extern const char kPaymentDetailsNotObject[]; + +// Payment handler passed a non-stringifiable field "details" in response to the +// "paymentrequest" event. +extern const char kPaymentDetailsStringifyError[]; + +// Used when the browser failed to fire the "paymentrequest" event without any +// actionable corrective action from the web developer. +extern const char kPaymentEventBrowserError[]; + +// Service worker timed out or stopped for some reason or was killed before the +// payment handler could respond to the "paymentrequest" event. +extern const char kPaymentEventServiceWorkerError[]; + +// Service worker timed out while responding to "paymentrequest" event. +extern const char kPaymentEventTimeout[]; + +// Payment handler navigated to a page with insecure context, invalid SSL, or +// malicious content. +extern const char kPaymentHandlerInsecureNavigation[]; + +// Payment handler encountered an internal error when handling the +// "paymentrequest" event. +extern const char kPaymentEventInternalError[]; + +// Payment handler rejected the promise passed into +// PaymentRequestEvent.respondWith() method. +extern const char kPaymentEventRejected[]; + +// The format for the message about a single payment method that is not +// supported. This format should be used with base::ReplaceChars() function, +// where "$" is the character to replace. +extern const char kSinglePaymentMethodNotSupportedFormat[]; + +} // namespace errors +} // namespace payments + +#endif // COMPONENTS_PAYMENTS_CORE_NATIVE_ERROR_STRINGS_H_
diff --git a/components/previews/content/BUILD.gn b/components/previews/content/BUILD.gn index efeb0d2e..5e13941 100644 --- a/components/previews/content/BUILD.gn +++ b/components/previews/content/BUILD.gn
@@ -16,8 +16,6 @@ "previews_decider_impl.h", "previews_hints.cc", "previews_hints.h", - "previews_hints_util.cc", - "previews_hints_util.h", "previews_optimization_guide.cc", "previews_optimization_guide.h", "previews_top_host_provider.h", @@ -53,7 +51,6 @@ "hints_fetcher_unittest.cc", "previews_decider_impl_unittest.cc", "previews_hints_unittest.cc", - "previews_hints_util_unittest.cc", "previews_optimization_guide_unittest.cc", "previews_ui_service_unittest.cc", "previews_user_data_unittest.cc",
diff --git a/components/previews/content/previews_hints.cc b/components/previews/content/previews_hints.cc index 286a9e3d..00a26c6 100644 --- a/components/previews/content/previews_hints.cc +++ b/components/previews/content/previews_hints.cc
@@ -14,6 +14,7 @@ #include "base/strings/stringprintf.h" #include "components/optimization_guide/hints_component_info.h" #include "components/optimization_guide/hints_component_util.h" +#include "components/optimization_guide/hints_processing_util.h" #include "components/previews/content/hint_update_data.h" #include "components/previews/core/bloom_filter.h" #include "components/previews/core/previews_features.h" @@ -405,7 +406,7 @@ } const optimization_guide::proto::PageHint* matched_page_hint = - FindPageHintForURL(url, hint); + optimization_guide::FindPageHintForURL(url, hint); if (!matched_page_hint) { return false; } @@ -413,7 +414,8 @@ for (const auto& optimization : matched_page_hint->whitelisted_optimizations()) { // Skip over any disabled experimental optimizations. - if (IsDisabledPerOptimizationHintExperiment(optimization)) { + if (optimization_guide::IsDisabledPerOptimizationHintExperiment( + optimization)) { continue; } if (!IsEnabledOptimizationType(optimization.optimization_type())) { @@ -492,7 +494,8 @@ // First find matched page hint. const optimization_guide::proto::PageHint* matched_page_hint = - FindPageHintForURL(url, hint_cache_->GetHintIfLoaded(url.host())); + optimization_guide::FindPageHintForURL( + url, hint_cache_->GetHintIfLoaded(url.host())); if (!matched_page_hint) { return false; } @@ -505,7 +508,8 @@ continue; } - if (IsDisabledPerOptimizationHintExperiment(optimization)) { + if (optimization_guide::IsDisabledPerOptimizationHintExperiment( + optimization)) { continue; } @@ -553,7 +557,7 @@ UMA_HISTOGRAM_ENUMERATION( "Previews.OptimizationGuide.HintCache.HostMatch.AtCommit", ect, net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST); - if (FindPageHintForURL(url, hint)) { + if (optimization_guide::FindPageHintForURL(url, hint)) { UMA_HISTOGRAM_ENUMERATION( "Previews.OptimizationGuide.HintCache.PageMatch.AtCommit", ect, net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_LAST);
diff --git a/components/previews/content/previews_hints.h b/components/previews/content/previews_hints.h index 84ba8f1..c30fb91 100644 --- a/components/previews/content/previews_hints.h +++ b/components/previews/content/previews_hints.h
@@ -11,9 +11,9 @@ #include "base/macros.h" #include "base/sequence_checker.h" +#include "components/optimization_guide/hints_processing_util.h" #include "components/optimization_guide/proto/hints.pb.h" #include "components/previews/content/hint_cache.h" -#include "components/previews/content/previews_hints_util.h" #include "components/previews/content/previews_user_data.h" #include "components/previews/core/host_filter.h" #include "net/nqe/effective_connection_type.h"
diff --git a/components/previews/content/previews_hints_unittest.cc b/components/previews/content/previews_hints_unittest.cc index 989215b..b65158f 100644 --- a/components/previews/content/previews_hints_unittest.cc +++ b/components/previews/content/previews_hints_unittest.cc
@@ -21,7 +21,6 @@ #include "components/previews/content/hint_cache.h" #include "components/previews/content/hint_cache_store.h" #include "components/previews/content/hint_update_data.h" -#include "components/previews/content/previews_hints_util.h" #include "components/previews/content/proto_database_provider_test_base.h" #include "components/previews/core/previews_features.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc index 1a48d9aa..1a98ee65 100644 --- a/components/previews/content/previews_optimization_guide.cc +++ b/components/previews/content/previews_optimization_guide.cc
@@ -24,7 +24,6 @@ #include "components/previews/content/hint_cache_store.h" #include "components/previews/content/hints_fetcher.h" #include "components/previews/content/previews_hints.h" -#include "components/previews/content/previews_hints_util.h" #include "components/previews/content/previews_top_host_provider.h" #include "components/previews/content/previews_user_data.h" #include "components/previews/core/previews_constants.h"
diff --git a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm index c85ae58..360ec8b 100644 --- a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm +++ b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
@@ -272,8 +272,7 @@ } void ProfileOAuth2TokenServiceIOSDelegate::ReloadAccountsFromSystem( - const CoreAccountId& primary_account_id) { - DCHECK(!primary_account_id.empty()); + const CoreAccountId& /* ignored */) { ReloadCredentials(); }
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc index 57ecdd1..557c6d1 100644 --- a/components/signin/public/identity_manager/identity_manager.cc +++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -416,10 +416,6 @@ gaia_cookie_manager_service_->ForceOnCookieChangeProcessing(); } -CoreAccountId IdentityManager::LegacySeedAccountInfo(const AccountInfo& info) { - return account_tracker_service_->SeedAccountInfo(info); -} - void IdentityManager::LegacyAddAccountFromSystem( const CoreAccountId& account_id) { token_service_->GetDelegate()->AddAccountFromSystem(account_id);
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h index b15a173..4d26ae6 100644 --- a/components/signin/public/identity_manager/identity_manager.h +++ b/components/signin/public/identity_manager/identity_manager.h
@@ -408,15 +408,6 @@ // or move it to the network::CookieManager. void ForceTriggerOnCookieChange(); - // Seeds the account whose account_id is given by - // AccountTrackerService::PickAccountIdForAccount() with its corresponding - // account information. Returns the same value PickAccountIdForAccount() - // when given the same arguments. - // NOTE: In normal usage, this method SHOULD NOT be called for getting the - // account id. It's only for replacement of production code. - // TODO(https://crbug.com/926940): Eliminate the need to expose this. - CoreAccountId LegacySeedAccountInfo(const AccountInfo& info); - // Adds a given account to the token service from a system account. This // API calls OAuth2TokenServiceDelegate::AddAccountFromSystem and it // triggers platform specific implementation for IOS.
diff --git a/components/signin/public/identity_manager/identity_manager_unittest.cc b/components/signin/public/identity_manager/identity_manager_unittest.cc index 560ceef..945402f 100644 --- a/components/signin/public/identity_manager/identity_manager_unittest.cc +++ b/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -1291,29 +1291,6 @@ #endif #if defined(OS_IOS) -TEST_F(IdentityManagerTest, LegacySeedAccountInfo) { - ASSERT_FALSE( - identity_manager() - ->FindAccountInfoForAccountWithRefreshTokenByEmailAddress(kTestEmail2) - .has_value()); - ASSERT_FALSE( - identity_manager() - ->FindAccountInfoForAccountWithRefreshTokenByGaiaId(kTestGaiaId2) - .has_value()); - - AccountInfo input_info; - input_info.email = kTestEmail2; - input_info.gaia = kTestGaiaId2; - const std::string account_id = - identity_manager()->LegacySeedAccountInfo(input_info); - - AccountInfo account_info = account_tracker()->GetAccountInfo(account_id); - - EXPECT_EQ(account_info.account_id, account_id); - EXPECT_EQ(account_info.email, kTestEmail2); - EXPECT_EQ(account_info.gaia, kTestGaiaId2); -} - TEST_F(IdentityManagerTest, ForceTriggerOnCookieChange) { base::RunLoop run_loop; identity_manager_observer()->SetOnAccountsInCookieUpdatedCallback(
diff --git a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java index 4dd17dd2..8b2c68c 100644 --- a/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java +++ b/components/test/android/browsertests_apk/src/org/chromium/components_browsertests_apk/ComponentsBrowserTestsApplication.java
@@ -8,6 +8,7 @@ import org.chromium.base.PathUtils; import org.chromium.native_test.NativeBrowserTestApplication; +import org.chromium.ui.base.ResourceBundle; /** * A basic content_public.browser.tests {@link android.app.Application}. @@ -21,6 +22,7 @@ if (isBrowserProcess()) { // Test-only stuff, see also NativeUnitTest.java. + ResourceBundle.setNoAvailableLocalePaks(); PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); } }
diff --git a/components/test/data/autofill/autofill_credit_card_form.html b/components/test/data/autofill/autofill_credit_card_form.html index f3ea33d..73184eb 100644 --- a/components/test/data/autofill/autofill_credit_card_form.html +++ b/components/test/data/autofill/autofill_credit_card_form.html
@@ -1,40 +1,50 @@ -<form name="cc1.1" id="cc1" action="https://example.com/" method="post"> - Name on card: <input autofocus="" type="text" name="name" id="credit_card_name"><br> - Credit card number: <input type="text" name="CCNo" id="credit_card_number"><br> - Expiry Date: <select name="CCExpiresMonth"> - <option value="01">01</option> - <option value="02">02</option> - <option value="03">03</option> - <option value="04">04</option> - <option value="05">05</option> - <option value="06">06</option> - <option value="07">07</option> - <option value="08">08</option> - <option value="09">09</option> - <option value="10">10</option> - <option value="11">11</option> - <option value="12">12</option> - </select> - / -<select name="CCExpiresYear"> - <option value="2010">2010</option> - <option value="2011">2011</option> - <option value="2012">2012</option> - <option value="2013">2013</option> - <option value="2014">2014</option> - <option value="2015">2015</option> - <option value="2016">2016</option> - <option value="2017">2017</option> - <option value="2018">2018</option> - <option value="2019">2019</option> - <option value="2020">2020</option> - <option value="2021">2021</option> - <option value="2022">2022</option> - <option value="2023">2023</option> - </select> - <br> - CVC: <input name="cvc"> -<br> - <input type="reset" value="Reset"> - <input type="submit" value="Submit" id="credit_card_submit"> -</form> \ No newline at end of file +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> +<html> + <!-- The following call prevents the zoom-on-focus animation which caused test flakes. --> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <head> + <title>Credit Card Test Form</title> + </head> + <body> + <form name="cc1.1" id="cc1" action="https://example.com/" method="post"> + Name on card: <input autofocus="" type="text" name="name" id="credit_card_name"><br> + Credit card number: <input type="text" name="CCNo" id="credit_card_number"><br> + Expiry Date: <select name="CCExpiresMonth"> + <option value="01">01</option> + <option value="02">02</option> + <option value="03">03</option> + <option value="04">04</option> + <option value="05">05</option> + <option value="06">06</option> + <option value="07">07</option> + <option value="08">08</option> + <option value="09">09</option> + <option value="10">10</option> + <option value="11">11</option> + <option value="12">12</option> + </select> + / + <select name="CCExpiresYear"> + <option value="2010">2010</option> + <option value="2011">2011</option> + <option value="2012">2012</option> + <option value="2013">2013</option> + <option value="2014">2014</option> + <option value="2015">2015</option> + <option value="2016">2016</option> + <option value="2017">2017</option> + <option value="2018">2018</option> + <option value="2019">2019</option> + <option value="2020">2020</option> + <option value="2021">2021</option> + <option value="2022">2022</option> + <option value="2023">2023</option> + </select> + <br> + CVC: <input name="cvc"> + <br> + <input type="reset" value="Reset"> + <input type="submit" value="Submit" id="credit_card_submit"> + </form> + </body> +</html>
diff --git a/components/variations/metrics.h b/components/variations/metrics.h index 5fe2b8c..660a4751 100644 --- a/components/variations/metrics.h +++ b/components/variations/metrics.h
@@ -47,16 +47,21 @@ FAILED_PARSE, FAILED_SIGNATURE, FAILED_GZIP, - // DELTA_COUNT is not so much a result of the seed store, but rather counting - // the number of delta-compressed seeds the SeedStore() function saw. Kept in - // the same histogram for convenience of comparing against the other values. - DELTA_COUNT, + DELTA_COUNT_OBSOLETE, FAILED_DELTA_READ_SEED, FAILED_DELTA_APPLY, FAILED_DELTA_STORE, FAILED_UNGZIP, FAILED_EMPTY_GZIP_CONTENTS, FAILED_UNSUPPORTED_SEED_FORMAT, + // The following are not so much a result of the seed store, but rather + // counting the types of seeds the SeedStore() function saw. Kept in the same + // histogram for efficiency and convenience of comparing against the other + // values. + GZIP_DELTA_COUNT, + NON_GZIP_DELTA_COUNT, + GZIP_FULL_COUNT, + NON_GZIP_FULL_COUNT, ENUM_SIZE };
diff --git a/components/variations/variations_http_header_provider.cc b/components/variations/variations_http_header_provider.cc index 24c43fc..afef2d0 100644 --- a/components/variations/variations_http_header_provider.cc +++ b/components/variations/variations_http_header_provider.cc
@@ -194,8 +194,6 @@ DCHECK(base::ThreadTaskRunnerHandle::IsSet()); base::FieldTrialList::AddObserver(this); - base::TimeTicks before_time = base::TimeTicks::Now(); - base::FieldTrial::ActiveGroups initial_groups; base::FieldTrialList::GetActiveFieldTrialGroups(&initial_groups); @@ -209,11 +207,6 @@ } UpdateVariationIDsHeaderValue(); - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Variations.HeaderConstructionTime", - (base::TimeTicks::Now() - before_time).InMicroseconds(), 1, - base::TimeDelta::FromSeconds(1).InMicroseconds(), 50); - variation_ids_cache_initialized_ = true; }
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc index 152a47c..f9ae86c 100644 --- a/components/variations/variations_seed_store.cc +++ b/components/variations/variations_seed_store.cc
@@ -151,9 +151,18 @@ bool is_gzip_compressed, bool fetched_insecurely, VariationsSeed* parsed_seed) { - UMA_HISTOGRAM_BOOLEAN("Variations.StoreSeed.HasCountry", - !country_code.empty()); + UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DataSize", + data.length() / 1024); + if (is_delta_compressed && is_gzip_compressed) { + RecordStoreSeedResult(StoreSeedResult::GZIP_DELTA_COUNT); + } else if (is_delta_compressed) { + RecordStoreSeedResult(StoreSeedResult::NON_GZIP_DELTA_COUNT); + } else if (is_gzip_compressed) { + RecordStoreSeedResult(StoreSeedResult::GZIP_FULL_COUNT); + } else { + RecordStoreSeedResult(StoreSeedResult::NON_GZIP_FULL_COUNT); + } // If the data is gzip compressed, first uncompress it. std::string ungzipped_data; if (is_gzip_compressed) { @@ -162,12 +171,6 @@ RecordStoreSeedResult(StoreSeedResult::FAILED_EMPTY_GZIP_CONTENTS); return false; } - - int size_reduction = ungzipped_data.length() - data.length(); - UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.GzipSize.ReductionPercent", - 100 * size_reduction / ungzipped_data.length()); - UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.GzipSize", - data.length() / 1024); } else { RecordStoreSeedResult(StoreSeedResult::FAILED_UNGZIP); return false; @@ -177,19 +180,12 @@ } if (!is_delta_compressed) { - const bool result = StoreSeedDataNoDelta( - ungzipped_data, base64_seed_signature, country_code, date_fetched, - fetched_insecurely, parsed_seed); - if (result) { - UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.Size", - ungzipped_data.length() / 1024); - } - return result; + return StoreSeedDataNoDelta(ungzipped_data, base64_seed_signature, + country_code, date_fetched, fetched_insecurely, + parsed_seed); } // If the data is delta compressed, first decode it. - RecordStoreSeedResult(StoreSeedResult::DELTA_COUNT); - std::string existing_seed_data; std::string updated_seed_data; LoadSeedResult read_result = @@ -207,17 +203,8 @@ const bool result = StoreSeedDataNoDelta( updated_seed_data, base64_seed_signature, country_code, date_fetched, fetched_insecurely, parsed_seed); - if (result) { - // Note: |updated_seed_data.length()| is guaranteed to be non-zero, else - // result would be false. - int size_reduction = updated_seed_data.length() - ungzipped_data.length(); - UMA_HISTOGRAM_PERCENTAGE("Variations.StoreSeed.DeltaSize.ReductionPercent", - 100 * size_reduction / updated_seed_data.length()); - UMA_HISTOGRAM_COUNTS_1000("Variations.StoreSeed.DeltaSize", - ungzipped_data.length() / 1024); - } else { + if (!result) RecordStoreSeedResult(StoreSeedResult::FAILED_DELTA_STORE); - } return result; }
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc index 31cd773..fc92f25 100644 --- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc +++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
@@ -276,6 +276,7 @@ states = base::CollapseWhitespaceASCII(states, false); base::ReplaceChars(states, " ", ",", &states); log += base::StringPrintf(" %s", states.c_str()); + g_object_unref(state_set); OnEvent(log); }
diff --git a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc index 6fa54a04..209bfc8 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_auralinux.cc
@@ -509,6 +509,7 @@ states->AppendString(atk_state_type_get_name(state_type)); } dict->Set("states", std::move(states)); + g_object_unref(state_set); AtkRelationSet* relation_set = atk_object_ref_relation_set(atk_object); auto relations = std::make_unique<base::ListValue>(); @@ -518,6 +519,7 @@ relations->AppendString(atk_relation_type_get_name(relation_type)); } dict->Set("relations", std::move(relations)); + g_object_unref(relation_set); AtkAttributeSet* attributes = atk_object_get_attributes(atk_object); for (AtkAttributeSet* attr = attributes; attr; attr = attr->next) {
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.h b/content/browser/accessibility/dump_accessibility_browsertest_base.h index a97efe3..b731dc2 100644 --- a/content/browser/accessibility/dump_accessibility_browsertest_base.h +++ b/content/browser/accessibility/dump_accessibility_browsertest_base.h
@@ -8,10 +8,8 @@ #include <string> #include <vector> -#include "base/debug/leak_annotations.h" #include "base/strings/string16.h" #include "base/test/scoped_feature_list.h" -#include "build/build_config.h" #include "content/browser/accessibility/accessibility_event_recorder.h" #include "content/public/browser/accessibility_tree_formatter.h" #include "content/public/test/content_browser_test.h" @@ -112,11 +110,6 @@ // The node filters loaded from the test file. std::vector<AccessibilityTreeFormatter::NodeFilter> node_filters_; -#if defined(LEAK_SANITIZER) && !defined(OS_NACL) - // http://crbug.com/568674 - ScopedLeakSanitizerDisabler lsan_disabler; -#endif - // The current tree-formatter and event-recorder factories. AccessibilityTreeFormatter::FormatterFactory formatter_factory_; AccessibilityEventRecorder::EventRecorderFactory event_recorder_factory_;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index c3a940a6..8a7483d 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -6703,10 +6703,11 @@ // specific cases. Check if this is one of those cases. bool is_commit_allowed_to_proceed = false; - // 1) This was a renderer-initiated navigation to a URL that doesn't need - // to be handled by the network stack (eg. about:blank). + // 1) This was a renderer-initiated navigation to an empty document. Most + // of the time: about:blank. is_commit_allowed_to_proceed |= - !IsURLHandledByNetworkStack(validated_params->url); + validated_params->url.SchemeIs(url::kAboutScheme) && + validated_params->url != GURL(url::kAboutSrcdocURL); // 2) This was a same-document navigation. // TODO(clamy): We should enforce having a request on browser-initiated
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc index 7047abbb..d150c51 100644 --- a/content/browser/navigation_browsertest.cc +++ b/content/browser/navigation_browsertest.cc
@@ -116,11 +116,19 @@ intercepted_navigations_.push_back(navigation_request); intercepted_messages_.push_back(*params); intercepted_requests_.push_back( - std::move((*interface_params)->interface_provider_request)); - intercepted_broker_content_requests_.push_back(std::move( - (*interface_params)->document_interface_broker_content_request)); - intercepted_broker_blink_requests_.push_back(std::move( - (*interface_params)->document_interface_broker_blink_request)); + *interface_params + ? std::move((*interface_params)->interface_provider_request) + : nullptr); + intercepted_broker_content_requests_.push_back( + *interface_params + ? std::move((*interface_params) + ->document_interface_broker_content_request) + : nullptr); + intercepted_broker_blink_requests_.push_back( + *interface_params + ? std::move( + (*interface_params)->document_interface_broker_blink_request) + : nullptr); if (loop_) loop_->Quit(); // Do not send the message to the RenderFrameHostImpl. @@ -2022,17 +2030,14 @@ NavigationHandleObserver handle_observer(shell()->web_contents(), GURL(url)); // TODO(arthursonzogni): It shouldn't be possible to navigate to - // about:srcdoc by executing location.href="about:srcdoc". Other web + // about:srcdoc by executing location.href= "about:srcdoc". Other web // browsers like Firefox aren't allowing this. EXPECT_TRUE(ExecJs(main_frame, JsReplace("location.href = $1", url))); - start_observer.Wait(); WaitForLoadStop(shell()->web_contents()); - - // TODO(arthursonzogni): The navigation should be blocked. EXPECT_TRUE(handle_observer.has_committed()); - EXPECT_FALSE(handle_observer.is_error()); - EXPECT_EQ(net::OK, handle_observer.net_error_code()); + EXPECT_TRUE(handle_observer.is_error()); + EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code()); } // 2. Subframe navigations to variations of about:srcdoc are blocked. @@ -2046,17 +2051,14 @@ GURL(url)); FrameTreeNode* subframe = main_frame->child_at(0); // TODO(arthursonzogni): It shouldn't be possible to navigate to - // about:srcdoc by executing location.href="about:srcdoc". Other web + // about:srcdoc by executing location.href= "about:srcdoc". Other web // browsers like Firefox aren't allowing this. EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = $1", url))); - start_observer.Wait(); WaitForLoadStop(shell()->web_contents()); - - // TODO(arthursonzogni): The navigation should be blocked. EXPECT_TRUE(handle_observer.has_committed()); - EXPECT_FALSE(handle_observer.is_error()); - EXPECT_EQ(net::OK, handle_observer.net_error_code()); + EXPECT_TRUE(handle_observer.is_error()); + EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code()); } // 3. Subframe navigation to about:srcdoc are not blocked. @@ -2083,4 +2085,27 @@ } } +// Test renderer initiated navigations to about:srcdoc are routed through the +// browser process. It means RenderFrameHostImpl::BeginNavigation() is called. +IN_PROC_BROWSER_TEST_P(NavigationBrowserTest, AboutSrcDocUsesBeginNavigation) { + GURL url(embedded_test_server()->GetURL("/title1.html")); + NavigateToURL(shell(), url); + + // If DidStartNavigation is called before DidCommitProvisionalLoad, then it + // means the navigation was driven by the browser process, otherwise by the + // renderer process. This tests it was driven by the browser process: + InterceptAndCancelDidCommitProvisionalLoad interceptor( + shell()->web_contents()); + DidStartNavigationObserver observer(shell()->web_contents()); + + EXPECT_TRUE(ExecJs(shell(), R"( + let iframe = document.createElement("iframe"); + iframe.srcdoc = "foo" + document.body.appendChild(iframe); + )")); + + observer.Wait(); // BeginNavigation is called. + interceptor.Wait(1); // DidCommitNavigation is called. +} + } // namespace content
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc index 6ae82179..74b9766d 100644 --- a/content/browser/notifications/platform_notification_context_impl.cc +++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -581,8 +581,9 @@ if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) DestroyDatabase(); - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(std::move(callback), display_count)); + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI, base::TaskPriority::USER_VISIBLE}, + base::BindOnce(std::move(callback), display_count)); } void PlatformNotificationContextImpl::ReadNotificationResources(
diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc index c233385..adc6c6e 100644 --- a/content/browser/payments/payment_app_provider_impl.cc +++ b/content/browser/payments/payment_app_provider_impl.cc
@@ -12,6 +12,7 @@ #include "base/base64.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/task/post_task.h" @@ -206,6 +207,8 @@ } else if (service_worker_status == blink::ServiceWorkerStatusCode::kErrorTimeout) { response_type = PaymentEventResponseType::PAYMENT_EVENT_TIMEOUT; + UMA_HISTOGRAM_BOOLEAN("PaymentRequest.ServiceWorkerStatusCodeTimeout", + true); } RespondWithErrorAndDeleteSelf(response_type);
diff --git a/content/browser/renderer_host/input/fling_browsertest.cc b/content/browser/renderer_host/input/fling_browsertest.cc index a2c8ecc..c9b521f9 100644 --- a/content/browser/renderer_host/input/fling_browsertest.cc +++ b/content/browser/renderer_host/input/fling_browsertest.cc
@@ -464,7 +464,7 @@ SynchronizeThreads(); // Wait for hit test data to change after scroll happens. - observer.WaitForHitTestDataChange(); + observer.WaitForTransformChangeInHitTestData(); // Fling and wait for the parent to scroll up. auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc index a0a22330..3025bd30 100644 --- a/content/browser/scheduler/browser_task_executor.cc +++ b/content/browser/scheduler/browser_task_executor.cc
@@ -12,6 +12,7 @@ #include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" +#include "build/build_config.h" #include "content/browser/browser_process_sub_thread.h" #include "content/browser/browser_thread_impl.h" #include "content/public/browser/browser_task_traits.h" @@ -55,7 +56,7 @@ return QueueType::kBestEffort; case base::TaskPriority::USER_VISIBLE: - return QueueType::kDefault; + return QueueType::kUserVisible; case base::TaskPriority::USER_BLOCKING: return QueueType::kUserBlocking;
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc index 9b7673e..76d783e 100644 --- a/content/browser/scheduler/browser_task_executor_unittest.cc +++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -30,6 +30,7 @@ namespace content { using ::base::TaskPriority; +using ::testing::ElementsAre; using ::testing::Invoke; using ::testing::Mock; using ::testing::SizeIs; @@ -111,7 +112,7 @@ EXPECT_EQ(GetQueueType({ID, TaskPriority::BEST_EFFORT}), QueueType::kBestEffort); EXPECT_EQ(GetQueueType({ID, TaskPriority::USER_VISIBLE}), - QueueType::kDefault); + QueueType::kUserVisible); EXPECT_EQ(GetQueueType({ID, TaskPriority::USER_BLOCKING}), QueueType::kUserBlocking); @@ -138,6 +139,27 @@ CheckExpectations<BrowserThread::IO>(); } +TEST_F(BrowserTaskTraitsMappingTest, + UIThreadTaskRunnerHasSamePriorityAsUIBlocking) { + auto ui_blocking = base::CreateSingleThreadTaskRunner( + {BrowserThread::UI, TaskPriority::USER_BLOCKING}); + auto thread_task_runner = base::ThreadTaskRunnerHandle::Get(); + + std::vector<int> order; + ui_blocking->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(1); })); + thread_task_runner->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(10); })); + ui_blocking->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(2); })); + thread_task_runner->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(20); })); + + base::RunLoop().RunUntilIdle(); + + EXPECT_THAT(order, ElementsAre(1, 10, 2, 20)); +} + class BrowserTaskExecutorWithCustomSchedulerTest : public testing::Test { private: class ScopedTaskEnvironmentWithCustomScheduler
diff --git a/content/browser/scheduler/browser_task_queues.cc b/content/browser/scheduler/browser_task_queues.cc index 63d273f..ec8a5c1 100644 --- a/content/browser/scheduler/browser_task_queues.cc +++ b/content/browser/scheduler/browser_task_queues.cc
@@ -61,6 +61,8 @@ return "ui_default_tq"; case BrowserTaskQueues::QueueType::kUserBlocking: return "ui_user_blocking_tq"; + case BrowserTaskQueues::QueueType::kUserVisible: + return "ui_user_visible_tq"; } } @@ -76,6 +78,8 @@ return "io_default_tq"; case BrowserTaskQueues::QueueType::kUserBlocking: return "io_user_blocking_tq"; + case BrowserTaskQueues::QueueType::kUserVisible: + return "io_user_visible_tq"; } } @@ -213,6 +217,9 @@ base::sequence_manager::TaskQueue::Spec(GetDefaultQueueName(thread_id)) .SetTimeDomain(time_domain)); + GetBrowserTaskQueue(QueueType::kUserVisible) + ->SetQueuePriority(QueuePriority::kLowPriority); + // Best effort queue GetBrowserTaskQueue(QueueType::kBestEffort) ->SetQueuePriority(QueuePriority::kBestEffortPriority);
diff --git a/content/browser/scheduler/browser_task_queues.h b/content/browser/scheduler/browser_task_queues.h index 3d2c1a7..eb93219 100644 --- a/content/browser/scheduler/browser_task_queues.h +++ b/content/browser/scheduler/browser_task_queues.h
@@ -41,7 +41,7 @@ enum class QueueType { // Catch all for tasks that don't fit other categories. // TODO(alexclarke): Introduce new semantic types as needed to minimize the - // number of default tasks. + // number of default tasks. Has the same priority as kUserBlocking. kDefault, // For non-urgent work, that will only execute if there's nothing else to @@ -55,11 +55,17 @@ // For navigation and preconnection related tasks. kNavigationAndPreconnection, - // A generic high priority queue. Long term we should replace this with - // additional semantic annotations. + // base::TaskPriority::kUserBlocking maps to this task queue. It's for tasks + // that affect the UI immediately after a user interaction. Has the same + // priority as kDefault. kUserBlocking, - kMaxValue = kUserBlocking + // base::TaskPriority::kUserVisible maps to this task queue. The result of + // these tasks are visible to the user (in the UI or as a side-effect on the + // system) but they are not an immediate response to a user interaction. + kUserVisible, + + kMaxValue = kUserVisible }; static constexpr size_t kNumQueueTypes =
diff --git a/content/browser/webrtc/webrtc_audio_browsertest.cc b/content/browser/webrtc/webrtc_audio_browsertest.cc index e46d2614..221f1474 100644 --- a/content/browser/webrtc/webrtc_audio_browsertest.cc +++ b/content/browser/webrtc/webrtc_audio_browsertest.cc
@@ -90,8 +90,45 @@ base::test::ScopedFeatureList audio_service_features_; }; +#if defined(OS_MACOSX) + +// Flaky on MacOS: http://crbug.com/982421 +#define MAYBE_CanMakeVideoCallAndThenRenegotiateToAudio \ + DISABLED_CanMakeVideoCallAndThenRenegotiateToAudio +#define MAYBE_EstablishAudioVideoCallAndEnsureAudioIsPlaying \ + DISABLED_EstablishAudioVideoCallAndEnsureAudioIsPlaying +#define MAYBE_EstablishAudioOnlyCallAndEnsureAudioIsPlaying \ + DISABLED_EstablishAudioOnlyCallAndEnsureAudioIsPlaying +#define MAYBE_EstablishIsac16KCallAndEnsureAudioIsPlaying \ + DISABLED_EstablishIsac16KCallAndEnsureAudioIsPlaying +#define MAYBE_EnsureLocalVideoMuteDoesntMuteAudio \ + DISABLED_EnsureLocalVideoMuteDoesntMuteAudio +#define MAYBE_EnsureRemoteVideoMuteDoesntMuteAudio \ + DISABLED_EnsureRemoteVideoMuteDoesntMuteAudio +#define MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks \ + DISABLED_EstablishAudioVideoCallAndVerifyUnmutingWorks + +#else + +#define MAYBE_CanMakeVideoCallAndThenRenegotiateToAudio \ + CanMakeVideoCallAndThenRenegotiateToAudio +#define MAYBE_EstablishAudioVideoCallAndEnsureAudioIsPlaying \ + EstablishAudioVideoCallAndEnsureAudioIsPlaying +#define MAYBE_EstablishAudioOnlyCallAndEnsureAudioIsPlaying \ + EstablishAudioOnlyCallAndEnsureAudioIsPlaying +#define MAYBE_EstablishIsac16KCallAndEnsureAudioIsPlaying \ + EstablishIsac16KCallAndEnsureAudioIsPlaying +#define MAYBE_EnsureLocalVideoMuteDoesntMuteAudio \ + EnsureLocalVideoMuteDoesntMuteAudio +#define MAYBE_EnsureRemoteVideoMuteDoesntMuteAudio \ + EnsureRemoteVideoMuteDoesntMuteAudio +#define MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks \ + EstablishAudioVideoCallAndVerifyUnmutingWorks + +#endif // defined(OS_MACOSX) + IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - CanMakeVideoCallAndThenRenegotiateToAudio) { + MAYBE_CanMakeVideoCallAndThenRenegotiateToAudio) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); std::string audio_only_constraints = BuildConstraints(kAudioConstraints, ""); @@ -101,7 +138,7 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EstablishAudioVideoCallAndEnsureAudioIsPlaying) { + MAYBE_EstablishAudioVideoCallAndEnsureAudioIsPlaying) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall("callAndEnsureAudioIsPlaying(" + @@ -109,7 +146,7 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EstablishAudioOnlyCallAndEnsureAudioIsPlaying) { + MAYBE_EstablishAudioOnlyCallAndEnsureAudioIsPlaying) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall("callAndEnsureAudioIsPlaying(" + @@ -117,7 +154,7 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EstablishIsac16KCallAndEnsureAudioIsPlaying) { + MAYBE_EstablishIsac16KCallAndEnsureAudioIsPlaying) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall( @@ -141,7 +178,7 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EnsureLocalVideoMuteDoesntMuteAudio) { + MAYBE_EnsureLocalVideoMuteDoesntMuteAudio) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall( @@ -149,7 +186,7 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EnsureRemoteVideoMuteDoesntMuteAudio) { + MAYBE_EnsureRemoteVideoMuteDoesntMuteAudio) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall( @@ -157,14 +194,14 @@ } IN_PROC_BROWSER_TEST_P(WebRtcAudioBrowserTest, - EstablishAudioVideoCallAndVerifyUnmutingWorks) { + MAYBE_EstablishAudioVideoCallAndVerifyUnmutingWorks) { std::string constraints = BuildConstraints(kAudioConstraints, kVideoConstraints); MakeAudioDetectingPeerConnectionCall("callAndEnsureAudioTrackUnmutingWorks(" + constraints + ");"); } -// We run these tests with the audio service both in and out of the the browser +// We run these tests with the audio service both in and out of the browser // process to have waterfall coverage while the feature rolls out. It should be // removed after launch. #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
diff --git a/content/common/swapped_out_messages.cc b/content/common/swapped_out_messages.cc index 24e0f0f..46d4da87 100644 --- a/content/common/swapped_out_messages.cc +++ b/content/common/swapped_out_messages.cc
@@ -5,9 +5,7 @@ #include "content/common/swapped_out_messages.h" #include "content/common/accessibility_messages.h" -#include "content/common/frame_messages.h" #include "content/common/view_messages.h" -#include "content/common/widget_messages.h" #include "content/public/common/content_client.h" namespace content { @@ -49,10 +47,6 @@ // Note that synchronous messages that are not handled will receive an // error reply instead, to avoid leaving the renderer in a stuck state. switch (msg.type()) { - // We allow closing even if we are in the process of swapping out. - case WidgetHostMsg_Close::ID: - // Sends an ACK. - case WidgetHostMsg_RequestSetBounds::ID: // Sends an ACK. case AccessibilityHostMsg_EventBundle::ID: return true;
diff --git a/content/public/test/hit_test_region_observer.cc b/content/public/test/hit_test_region_observer.cc index 8408dd2..47e391b 100644 --- a/content/public/test/hit_test_region_observer.cc +++ b/content/public/test/hit_test_region_observer.cc
@@ -121,7 +121,7 @@ HitTestTransformChangeObserver::~HitTestTransformChangeObserver() = default; -void HitTestTransformChangeObserver::WaitForHitTestDataChange() { +void HitTestTransformChangeObserver::WaitForTransformChangeInHitTestData() { DCHECK(!run_loop_); // If the transform has already changed then don't run RunLoop.
diff --git a/content/public/test/hit_test_region_observer.h b/content/public/test/hit_test_region_observer.h index 4a5780db..6c2a8227 100644 --- a/content/public/test/hit_test_region_observer.h +++ b/content/public/test/hit_test_region_observer.h
@@ -61,22 +61,19 @@ DISALLOW_COPY_AND_ASSIGN(HitTestRegionObserver); }; -// Test API which can wait until there is a change in hit test data for a -// particular FrameSinkId. The change can be one of the following: -// 1. Change in root to target transform. -// 1. Target that wasn't hit testable became hit testable. -// 2. Target that was hit testable became not hit testable. +// Test API which can wait until there is a transform change in hit test data +// for a particular FrameSinkId. i.e. change in root to target transform. class HitTestTransformChangeObserver : public viz::HitTestRegionObserver { public: explicit HitTestTransformChangeObserver( const viz::FrameSinkId& frame_sink_id); ~HitTestTransformChangeObserver() override; - // Waits until hit testing data for |frame_sink_id_| changes. If hit test data - // for |frame_sink_id_| has already changed since it was cached then this will - // return immediately. The cached transform will be updated when this returns - // so it can be called later. - void WaitForHitTestDataChange(); + // Waits until transform changes in hit testing data for |frame_sink_id_|. If + // hit test data for |frame_sink_id_| has already changed since it was cached + // then this will return immediately. The cached transform will be updated + // when this returns so it can be called later. + void WaitForTransformChangeInHitTestData(); private: // viz::HitTestRegionObserver:
diff --git a/content/renderer/media/webrtc/rtc_stats.cc b/content/renderer/media/webrtc/rtc_stats.cc index 921a0fa5..4075b55 100644 --- a/content/renderer/media/webrtc/rtc_stats.cc +++ b/content/renderer/media/webrtc/rtc_stats.cc
@@ -93,6 +93,17 @@ return stats_members; } +size_t CountWhitelistedStats( + const scoped_refptr<const webrtc::RTCStatsReport>& stats_report) { + size_t size = 0; + for (const auto& stats : *stats_report) { + if (IsWhitelistedStats(stats)) { + ++size; + } + } + return size; +} + } // namespace RTCStatsReport::RTCStatsReport( @@ -101,7 +112,8 @@ : stats_report_(stats_report), it_(stats_report_->begin()), end_(stats_report_->end()), - exposed_group_ids_(exposed_group_ids) { + exposed_group_ids_(exposed_group_ids), + size_(CountWhitelistedStats(stats_report)) { DCHECK(stats_report_); } @@ -135,10 +147,7 @@ } size_t RTCStatsReport::Size() const { - // TODO(crbug.com/908072): If there are non-whitelisted stats objects in the - // report, this would return the wrong thing; DCHECK that all objects are - // whitelisted or make this method return the whitelisted count - return stats_report_->size(); + return size_; } RTCStats::RTCStats(
diff --git a/content/renderer/media/webrtc/rtc_stats.h b/content/renderer/media/webrtc/rtc_stats.h index 478302a..826f10e 100644 --- a/content/renderer/media/webrtc/rtc_stats.h +++ b/content/renderer/media/webrtc/rtc_stats.h
@@ -37,6 +37,8 @@ webrtc::RTCStatsReport::ConstIterator it_; const webrtc::RTCStatsReport::ConstIterator end_; blink::WebVector<webrtc::NonStandardGroupId> exposed_group_ids_; + // Number of whitelisted webrtc::RTCStats in |stats_report_|. + const size_t size_; }; class CONTENT_EXPORT RTCStats : public blink::WebRTCStats {
diff --git a/content/renderer/media/webrtc/rtc_stats_unittest.cc b/content/renderer/media/webrtc/rtc_stats_unittest.cc index d4c165a..1b5de1b29 100644 --- a/content/renderer/media/webrtc/rtc_stats_unittest.cc +++ b/content/renderer/media/webrtc/rtc_stats_unittest.cc
@@ -42,6 +42,9 @@ new webrtc::RTCPeerConnectionStats(whitelisted_id, 42))); RTCStatsReport report(webrtc_report.get(), {}); + // Only whitelisted stats are counted. + EXPECT_EQ(report.Size(), 1u); + std::unique_ptr<blink::WebRTCStats> stats = report.Next(); EXPECT_TRUE(stats); EXPECT_EQ(stats->Id(), whitelisted_id);
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index cefb8a1..3967382 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -6555,12 +6555,11 @@ return; } - // Navigation to about:blank or to about:srcdoc don't need to consult the - // browser. The document content is already available in the renderer - // process. + // Navigation to about:blank don't need to consult the browser. The document + // content is already available in the renderer process. // TODO(arthursonzogni): Remove this. Everything should use the default code // path and be driven by the browser process. - if ((url.IsAboutSrcdoc() || WebDocumentLoader::WillLoadUrlAsEmpty(url)) && + if (WebDocumentLoader::WillLoadUrlAsEmpty(url) && !url.IsAboutSrcdoc() && !is_history_navigation_in_new_child_frame) { if (!frame_->WillStartNavigation( *info, false /* is_history_navigation_in_new_child_frame */))
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 53f942a..df49b0d 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -1237,8 +1237,13 @@ void BeginNavigationInternal(std::unique_ptr<blink::WebNavigationInfo> info, bool is_history_navigation_in_new_child_frame); - // Commit a navigation that isn't handled by the browser (e.g., an empty - // document, about:srcdoc or an MHTML archive). + // Used to load the initial empty document. This one is special, since it + // isn't the result of a navigation. + // + // TODO(arthursonzogni): This function is also used for renderer initiated + // navigations to about:blank pages. The browser initiated ones uses the + // normal code path in the browser process. Stop maintaining two code path by + // removing this one. void CommitSyncNavigation(std::unique_ptr<blink::WebNavigationInfo> info); // Commit navigation with |navigation_params| prepared. @@ -1753,9 +1758,8 @@ RenderFrameMediaPlaybackOptions renderer_media_playback_options_; - // Used by renderer initiated navigations not driven by the browser process: - // - navigation to about:blank. - // - navigation to about:srcdoc. + // Used by renderer initiated navigations to about:blank pages. This edge case + // doesn't use yet the normal code path in the browser process. // TODO(arthursonzogni): Remove this. Everything should use the default code // path and be driven by the browser process. base::CancelableOnceCallback<void()> sync_navigation_callback_;
diff --git a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java index d50ae20..d5d82815 100644 --- a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java +++ b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsApplication.java
@@ -8,6 +8,7 @@ import org.chromium.base.PathUtils; import org.chromium.native_test.NativeBrowserTestApplication; +import org.chromium.ui.base.ResourceBundle; /** * A basic content_public.browser.tests {@link android.app.Application}. @@ -22,6 +23,7 @@ if (isBrowserProcess()) { // Test-only stuff, see also NativeUnitTest.java. PathUtils.setPrivateDataDirectorySuffix(PRIVATE_DATA_DIRECTORY_SUFFIX); + ResourceBundle.setNoAvailableLocalePaks(); } } }
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java index f754e53..c9db10f 100644 --- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java +++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellApplication.java
@@ -13,6 +13,7 @@ import org.chromium.base.ContextUtils; import org.chromium.base.PathUtils; import org.chromium.base.multidex.ChromiumMultiDexInstaller; +import org.chromium.ui.base.ResourceBundle; /** * Entry point for the content shell application. Handles initialization of information that needs @@ -27,6 +28,7 @@ super.attachBaseContext(base); boolean isBrowserProcess = !ContextUtils.getProcessName().contains(":"); ContextUtils.initApplicationContext(this); + ResourceBundle.setNoAvailableLocalePaks(); if (isBrowserProcess) { if (BuildConfig.IS_MULTIDEX_ENABLED) { ChromiumMultiDexInstaller.install(this);
diff --git a/content/test/mock_platform_notification_service.cc b/content/test/mock_platform_notification_service.cc index be4638a..faa8e541 100644 --- a/content/test/mock_platform_notification_service.cc +++ b/content/test/mock_platform_notification_service.cc
@@ -90,7 +90,7 @@ displayed_notifications.insert(notification_id); base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::UI}, + FROM_HERE, {BrowserThread::UI, base::TaskPriority::USER_VISIBLE}, base::BindOnce(std::move(callback), std::move(displayed_notifications), true /* supports_synchronization */)); }
diff --git a/docs/media/media_router.md b/docs/media/media_router.md new file mode 100644 index 0000000..e0d90c3 --- /dev/null +++ b/docs/media/media_router.md
@@ -0,0 +1,432 @@ +# Chrome Media Router + +## Introduction + +*TODO: Update with integration with Remote Playback API* + +The media router is a component in Chrome responsible for matching clients that +wish to render media or URLs (_media sources_) on devices and endpoints capable +of rendering that content (_media sinks_). When a media source is linked with a +_media sink_, a _media route_ is created that allows two-way messaging between +the source and the sink. The media route allows the application to negotiate a +peer-to-peer media streaming session with the media sink via messaging (e.g., +via [WebRTC](http://www.webrtc.org/) or [Cast Streaming](https://cs.chromium.org/chromium/src/components/mirroring/)), +aka "mirroring." The media route can also be used to request the remote display +to render a URL without an associated peer-to-peer media streaming session, aka +"flinging". The media route can be terminated at user or browser request, +which denies access to the media sink from the application. + +The Web [Presentation API](http://w3c.github.io/presentation-api/) allows a Web +application to request display of Web content on a secondary (wired, or +wireless) screen. The content may be rendered locally and streamed to the +display or rendered remotely. The Web application controls the content by +two-way messaging. + +Note that the non-Blink parts of the media router will be implemented only in +desktop Chrome and ChromeOS. Presentation API functionality will be implemented +in Chrome for Android using analogous platform components such as the [Android +Media Route Provider +framework](https://developer.android.com/guide/topics/media/mediarouteprovider.html). + +*TODO: Add material on 1-UA mode, or add a separate document* + +Offscreen rendering, capture, and streaming of WebContents (required for full +Presentation API support) will be covered in a separate design document. + +## Objectives + +The objectives of this project: + +* Allow use of media sinks from a multitude of clients, including: Web + applications via the Presentation API; + Chrome apps; the browser itself; and the Chrome OS system shell. + +* Support mirroring locally rendered content to external screens, including + on-screen and off-screen tabs, Chrome apps windows, and the system desktop. + +* Support "flinging" HTML5 documents to remote devices capable of rendering + them. + +* Support the [Cast Chrome Sender + SDK](https://developers.google.com/cast/docs/reference/chrome/) on desktop and + Android without any user installed extensions. + +* Allow new types of media sinks to be added to Chrome by implementing + additional Media Route Providers. + +The following are non-goals but may be objectives for future work: + +* Multicast of local content to multiple sinks at once. + +* Support for third party media route providers in Javascript or + run-time installation of media route providers. + +* Support for sinks that are not primarily intended to render media. + +## Overview + +The media router consists of four distinct components: + +1. The Chrome Media Router is a browser service exposed in-process via C++ API +and is exposed to other processes via a set of two Mojo interfaces: the +Presentation interface and the Media Router API interface. Its job is to field +requests from clients for media sink availability, media route +construction/destruction, and media route control via messaging. It also +controls the Media Router Dialog and delegates many functions to the Media +Router component extension. + +2. *TODO: update* The Media Router extension is an external component extension +responsible for direct interaction with media sinks. The component extension +will initially support use of [Cast](http://www.google.com/cast/) and +[DIAL](http://www.dial-multiscreen.org/) devices with more types of sinks to be +added over time. The component extension interacts with the Chrome Media Router +via the Media Router API Mojo service, and uses some chrome.\* platform APIs, +such as chrome.dial, chrome.cast.channel, and chrome.mdns to implement network +level interaction with Cast and DIAL devices. + +3. Users interact with the Chrome Media Router through the Media Router +Dialog. This dialog allows users to choose the destination media sink +for new media routes and view and stop active media routes. It may be pulled up +by the user clicking the Cast icon in the browser toolbar, or at the request of +a Web application via the Presentation API. It is implemented in Views. + +4. The PresentationService mojo interface acts as a bridge between the Chrome +Media Router and the Blink implementations of the Presentation API ([launch +bug](https://code.google.com/p/chromium/issues/detail?id=412331)). + +## Architecture + +The following diagram illustrates the architecture of the components described above. + +*TODO: update diagram* + +[](https://www.chromium.org/developers/design-documents/media-router/Chrome%20Media%20Router%20Architecture%20%281%29.png?attredirects=0) + +### Chrome Media Router + +The Chrome Media Router is a browser-resident service that serves as a +media-protocol-agnostic platform for parties interested in media routing. It +provides its clients with a set of APIs for media routing related queries and +operations, including: + +* Register for notifications when a sink is available that can render a media + source. (Media sources are represented as URIs and can represent local media + or remotely hosted content.) + +* Request routing of media for that source, which will show the user the media + router dialog to select a compatible sink. If the user selects a sink, the + media route is returned to the application to allow it to control media + playback. + +* Accept media control actions from the Media Router Dialog for an active media + route and forwarding them to the associated route provider. + +* Send and receive arbitrary (string) messages between the application the media + sink. + +* Terminate media routes, and notify the client and media route provider when + that happens. + +The Chrome Media Router, itself, does not directly interact with media +sinks. *TODO: update* Instead it delegates these requests and responses to a +media route provider in the component extension. The Chrome Media Router will +contain bookkeeping of established routes, pending route requests, and other +related resources, so it does not have to request this information from the +route provider each time. + +The following pseudocode describes how a client of the Chrome Media Router +(through its C++ API) would use it to initiate and control a media sharing +session. + +### Media Router API Example + +*TODO: update or remove* + +``` +MediaRouter* media_router = MediaRouterImpl::GetInstance(); + +// Find out what screens are capable of rendering, e.g. www.youtube.com +MediaSource youtube_src = MediaSource::ForPresentationUrl("http://www.youtube.com"); + +// MyMediaSinksObserver should override MediaSinksObserver::OnSinksReceived to +// handle updates to the list of screens compatible with youtube_src +MediaSinksObserver* my_observer = new MyMediaSinksObserver(youtube_src); +media_router->RegisterObserver(my_observer); + +// Ask the user to pick a screen from the list passed to my_observer and +// capture the sink_id (code not shown) + +// Request routing of media for that source. |callback| is passed a +// MediaRouteResponse& that contains a MediaRoute result if successful. +media_router->StartRouteRequest(youtube_src, sink_id, callback); + +// The MediaRoute can be used to post messages to the sink. +media_router->PostMessage(media_route.media_route_id, "some data", "optional_extra_data_json"); + +// The MediaRoute can be closed which signals the sinkto terminate any remote +// app or media streaming session. +media_router->CloseRoute(media_route.media_route_id);` +``` + +The Media Router interacts with the component extension via a Mojo service, the +Media Router API, that exposes functionality whose implementation is delegated +to the extension. + +### Media Router API Mojo Interface + +*TODO: Update or replace with link* + +``` +// Interface for sending messages from the MediaRouter (MR) to the Media +// Router Provider Manager (MRPM). + +interface MediaRouterApiClient { + // Signals the media route manager to route the media located + // at |source_urn| to |sink_id|. + RequestRoute(int64 request_id, string source, string sink_id); + + // Signals the media route manager to close the route specified by |route_id|. + CloseRoute(string route_id); + + // Signals the media route manager to start querying for sinks + // capable of displaying |source|. + AddMediaSinksQuery(string source); + + // Signals the media route manager to stop querying for sinks + // capable of displaying |source|. + RemoveMediaSinksQuery(string source); + + // Sends |message| with optional |extra_info_json| via the media route + // |media_route_id|. + // |extra_info_json| is an empty string if no extra info is provided. + PostMessage(string media_route_id, string message, string extra_info_json); +}; + +// Interface for sending messages from the MRPM to the MR. +[Client=MediaRouterApiClient] +interface MediaRouterApi { + // Called when the provider manager is ready. + OnProviderManagerReady(string extension_id); + + // Called when the Media Route Manager receives a new list of sinks. + OnSinksReceived(string source, + array<MediaSink> sinks, + array<MediaRoute> routes); + + // Called after a MediaRoute is established. + OnRouteResponseReceived(int64 request_id, MediaRoute route); + + // Called when route establishment fails. + OnRouteResponseError(int64 request_id, string error_text); +}; +``` + +### Media Router Component Extension + +*TODO: update to discuss in-browser MRPs and Mirroring Service* + +The component extension manages discovery of and network interaction with +individual media sinks. For the purposes of this discussion a sink is a +LAN-connected device that speaks the Cast or DIAL protocol, but in theory it +could be any other type of endpoint that supports media rendering and two-way +messaging. The extension consists of three types of components: + +* Media Route Providers: Each provider is a Javascript bundle that knows how to + find and communicate with a specific type of media sink. It communicates + with the media sink using HTTP/XHR or via device-specific network protocols + (e.g., Cast Channel and Cast Streaming). + +* Media Route Provider Manager: This is responsible for dispatching requests + from the Chrome Media Router to individual providers. It also registers + providers on startup. + +* Mirroring Service: If a media source is requested that represents the tab or + desktop contents, this service acts on the behalf of the application to + initiate the mirroring session. This is handled internally to the component + extension and is not exposed to the rest of the browser, it appears to be + just another media route. + +The component extension is written in JavaScript and includes code for multiple +media route providers. Initially Media Route Providers will be implemented for +Cast and DIAL devices with others to follow. Over time media route providers +that do not rely on proprietary protocols will be implemented in the Chromium +repository. + +As an external component, the extension is installed on the initial run of the +browser. It is built around an event page so it registers itself with the Media +Router, registers itself with discovery APIs to be notified of display +availability, and then suspends. The component extension will only be active +when there are applications with pending sink availability requests or media +routes, or when there is active network traffic between the extension and a +media sink. + +There are several modules to the extension that are loaded on-demand. The main +event page bundles are a few hundred kb. The extension is updated on the Chrome +release cycle with a branch made a week or two after the Chrome branch point. + +## Tab/Desktop Mirroring + +*TODO: update with discussion of C++ MirroringService* + +Tab and desktop mirroring will request routing of a media source with URN like +urn:google:tab:3 representing tab contents. When the component extension +receives a request to route this source, the media route provider manager will +query route providers to enumerate sinks that can render streamed tab +contents. Once a sink is selected by the user, the mirroring service will create +the appropriate MediaStream using the chrome.tabCapture extension API. The +MediaStream will then be passed to a Cast Streaming or WebRTC session depending +on the preferred protocol of the selected sink. When the media route is +terminated, the associated streaming session and media capture are also +terminated. A similar approach will be used for desktop mirroring but using +chrome.desktopCapture instead. + +# Presentation API + +*TODO: Discuss 1-UA mode* +*TODO: Update for Onion Soup* + +Media routing of Web content will primarily be done through the Presentation +API. Some media sinks (e.g. Cast) can render a subset of Web content natively, +or render an equivalent app experience (e.g., via DIAL). For generic Web +documents, we plan on rendering it in an offscreen WebContents and then using +the Tab Mirroring approach outlined above. The design of the offscreen rendering +capability will be added later to this document. + +The Presentation API implementation in Blink will live in content/ and will +operate on the frame level. It will delegate the calls to the embedder's Media +Router implementation (Android Media Router / Chrome Media Router for Android / +Chrome, respectively) via a common PresentationServiceDelegate interface. A +draft Mojo interface follows (not yet complete): + +## PresentationService mojo interface + +*TODO: Update or replace with link* + +``` +interface PresentationService { + // Returns the last screen availability state if it’s changed since the last + // time the method was called. The client has to call this method again when + // handling the result (provided via Mojo callback) to get the next update + // about the availability status. + // May start discovery of the presentation screens. The implementation might + // stop discovery once there are no active calls to GetScreenAvailability. + // |presentation_url| can be specified to help the implementation to filter + // out incompatible screens. + GetScreenAvailability(string? presentation_url) => (bool available); + + // Called when the frame no longer listens to the + // |availablechange| event. + OnScreenAvailabilityListenerRemoved(); +}; +``` + +*TODO: Update table with current flow, or remove this section* + +Here is how the presentation API will roughly map to Chrome Media Router API: + +**Presentation API** + +**Chrome Media Router** + +Adding onavailablechange listener + +RegisterObserver(), with result propagated back to the RenderFrame / Presentation API. + +startSession + +Opens Media Router Dialog (via MediaRouterDialogController) -> User action -> StartRouteRequest() + +joinSession + +StartRouteRequest() + +postMessage + +PostMessage() + +close + +CloseRoute() + +Adding onmessage listener + +RegisterMessageObserver() (tentative) + +Adding onstatechange listener + +RegisterRouteStateChangeObserver() (tentative) + +## Media Router Dialog + +End user control of media routing is done through the Media Router Dialog. The +media router dialog is implemented in Views. + +The Media Router Dialog is activated by clicking on the Cast icon, which is +always available to the user. The Cast icon appears in the toolbar action menu +when there is an active media route, or when the user chooses to pin the icon +there permanently. + +----------------------------------------------- + +*TODO: Update screenshot* + +[](https://www.chromium.org/developers/design-documents/media-router/media_router_overflow.jpg?attredirects=0) + +Clicking on the Cast icon brings up a menu of available media sinks that are +compatible with the current content. For Web documents not using the +Presentation API, these will include sinks that can render tab or desktop +capture. For Web documents, it will include media sinks compatible with the URL +requested to be presented through the Presentation API. + +----------------------------------------------- + +*TODO: Update screenshot* + +[](https://www.chromium.org/developers/design-documents/media-router/media_router_screen_selector.jpg?attredirects=0) + +----------------------------------------------- + +## Offscreen Rendering + +TODO(miu) + +# Security + +*TODO: Update to discuss sandboxing of in-browser MRPs.* + +The entire project should be security reviewed from a holistic and architectural +perspective. Specific security-related aspects: + +* The Chrome Media Router will be designed to have a minimal processing of the + URIs and messages passed through it (perhaps only checking for syntactic + validity). +* The Media Router Dialog will allow the MRPs to inject custom + content into it, so for example, the inline controls for a game can differ + from those for a movie. This content will be rendered out-of-process in an + <extensionview> to prevent any escalation of privileges from compromised + content. +* The individual platform APIs used by the component extension MRPs + (chrome.dial, chrome.mdns, chrome.cast.channel, chrome.cast.streaming) have + been security reviewed previously. + +# Contact + +* [media-dev@chromium.org](mailto:media-dev@chromium.org) + +# Code location + +| Path | Description | +| ---------------------------------------|--------------------------------------| +| `chrome/browser/media/router` | Main implementation, in-browser MRPs | +| `chrome/common/media_router` | Mojo definitions, shared impl | +| `chrome/browser/ui/media_router/` | Media Router icon, dialog | +| `chrome/browser/ui/views/media_router/`| | +| `content/browser/presentation/` | Browser impl of Presentation API| +| `third_party/blink/renderer/modules/presentation/`| Blink impl of Presentaton API| + +# History + +| Date | Author | Description | +|------------|---------------------|--------------------------------------------------| +| 6-Feb-2015 | mfoltz@chromium.org | Initial publication | +| 8-Jul-2019 | mfoltz@chromium.org | Converted to markdown, obsolete material removed.|
diff --git a/docs/security/sheriff.md b/docs/security/sheriff.md index 48697fbd..a0d3e1f 100644 --- a/docs/security/sheriff.md +++ b/docs/security/sheriff.md
@@ -54,14 +54,18 @@ * Look at every incoming security bug report on the [dashboard](http://go/chrome-security-bugs). Ensure each is accurately triaged, and actively progressing towards getting fixed. +* Don't forget to fully triage the low severity bugs. Once a bug is labeled with + `Security_Severity-Low `, it disappears from the first sheet and may slip + under your radar. * Keep the [Sheriff Handoff Log](http://go/chrome-security-sheriff-handoff) up to date. * Shout for help if the incoming bug rate is too high ([suggested vocal exercises](https://youtu.be/5y_SbnPx_cE?t=37s)). The first person to ask is the marshal. -* Make sure all new bug reports are triaged completely. That means no red cells - on the dashboard. Double-check that OS are set properly. For most of the bugs, - more than one OS is affected, but the dashboard will not highlight it in red. +* Make sure all **new bug reports** are triaged completely. That means no red + cells on the top of the dashboard. Double-check that OS flags are set + properly. For most of the bugs, typically more than one OS is affected, but + the dashboard will not highlight it in red. * Stay sharp, keep in shape ([hand-stand pushups](https://www.youtube.com/watch?v=jZ1ZDlLImF8#t=50) are standard for the sheriff), and remember you may be [called upon during @@ -79,14 +83,16 @@ * Note: external emails will always come in on security@chromium.org as chrome-security@google.com is a Google-only list, but both need to be triaged. -* Change bugs status to **Fixed** for those that the developer forgets to - close. Make sure to read bug comments where developer might point out that it - needs more CLs, et c. Wait 24 hours before closing ClusterFuzz bugs, to give +* Change bugs status to **Fixed** for those that the developer forgets to close. + Make sure to read bug comments where developer might point out that it needs + more CLs, et c. Wait 24 hours before closing ClusterFuzz bugs, to give ClusterFuzz a chance to close it automatically. * [Starting point](https://bugs.chromium.org/p/chromium/issues/list?can=2&q=Type%3D%22Bug-Security%22+%22Change-Id:%22) -* Look at open security bug reports and check that progress is occurring. That - includes the Low Severity Bugs page of the dashboard. The rule of thumb is - *if there is any red cell on the dashboard, it needs your attention*. +* Look at the open security bug reports and check that progress is occurring. + This does not apply to the **new bug reports** (these are handled by the + sheriff), but does apply to the issues on the *Low Severity Bugs* page of the + dashboard. The rule of thumb is *if there is any red cell on the dashboard, it + needs your attention*. * Stay sharp, keep in shape ([finger exercises](https://youtu.be/20elMaVZ9lg?t=47s) are standard for the marshal), and remember you may be called upon during emergencies.
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 459123c..5e58b77 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1502,9 +1502,6 @@ <message name="IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_ACTION" desc="Text label to the Send Tab To Self action in the extension menu that sends the current tab to the user's other devices. [iOS only]"> Send To Your Devices </message> - <message name="IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_DEVICE_ACTION" desc="Text label for the Send Tab To Self action sheet that chooses the device to which the current tab will be sent. [iOS only]"> - Send <ph name="Title">$1<ex>Webpage Title</ex></ph> to one of your devices. - </message> <message name="IDS_IOS_SEND_TAB_TO_SELF_SNACKBAR_MESSAGE" desc="Message briefly displayed at the bottom of the screen to the user to inform that the tab is being sent to the target device. [Length: 35em]" meaning="The is being sent to the target device. [Length: 35em]"> Sending... </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_DEVICE_ACTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_DEVICE_ACTION.png.sha1 deleted file mode 100644 index 3ae3078..0000000 --- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_DEVICE_ACTION.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -4ddb290089c2d98e33e67adba7485c6993b74948 \ No newline at end of file
diff --git a/ios/chrome/browser/browser_state/browser_state_services_egtest.mm b/ios/chrome/browser/browser_state/browser_state_services_egtest.mm index 8b1dbca8..ac82feb 100644 --- a/ios/chrome/browser/browser_state/browser_state_services_egtest.mm +++ b/ios/chrome/browser/browser_state/browser_state_services_egtest.mm
@@ -28,9 +28,11 @@ // invoked. void OnGotPrimaryAccountInfo( bool* get_primary_account_info_callback_called_flag, - const base::Optional<CoreAccountInfo>& account_info, + const base::Optional<CoreAccountId>& account_id, + const base::Optional<std::string>& gaia, + const base::Optional<std::string>& email, const identity::AccountState& account_state) { - GREYAssert(!account_info, @"AccountInfo has unexpected value"); + GREYAssert(!account_id, @"AccountId has unexpected value"); *get_primary_account_info_callback_called_flag = true; }
diff --git a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm index 0e6c5632..2b5855d 100644 --- a/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm +++ b/ios/chrome/browser/payments/ios_payment_instrument_launcher.mm
@@ -13,7 +13,7 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "components/payments/core/error_strings.h" +#include "components/payments/core/native_error_strings.h" #include "components/payments/core/payment_details.h" #include "components/payments/core/payment_instrument.h" #import "ios/chrome/browser/payments/payment_request_constants.h"
diff --git a/ios/chrome/browser/signin/authentication_service.mm b/ios/chrome/browser/signin/authentication_service.mm index c2b57819..5bd7b1d 100644 --- a/ios/chrome/browser/signin/authentication_service.mm +++ b/ios/chrome/browser/signin/authentication_service.mm
@@ -287,6 +287,7 @@ void AuthenticationService::SignIn(ChromeIdentity* identity, const std::string& hosted_domain) { + DCHECK(!hosted_domain.empty()); DCHECK(ios::GetChromeBrowserProvider() ->GetChromeIdentityService() ->IsValidIdentity(identity)); @@ -294,33 +295,36 @@ ResetPromptForSignIn(); sync_setup_service_->PrepareForFirstSyncSetup(); - // The account info needs to be seeded for the primary account id before - // signing in. - AccountInfo info; - info.gaia = base::SysNSStringToUTF8([identity gaiaID]); - info.email = GetCanonicalizedEmailForIdentity(identity); - info.hosted_domain = hosted_domain; - std::string new_authenticated_account_id = - identity_manager_->LegacySeedAccountInfo(info); - std::string old_authenticated_account_id = - identity_manager_->GetPrimaryAccountId(); + const CoreAccountId account_id = identity_manager_->PickAccountIdForAccount( + base::SysNSStringToUTF8(identity.gaiaID), + GetCanonicalizedEmailForIdentity(identity)); + + // Load all credentials from SSO library. This must load the credentials + // for the primary account too. + identity_manager_->LegacyReloadAccountsFromSystem(); + + // Ensure that the account the user is trying to sign into has been loaded + // from the SSO library and that hosted_domain is set to the provided value. + const base::Optional<AccountInfo> account_info = + identity_manager_->FindAccountInfoForAccountWithRefreshTokenByAccountId( + account_id); + CHECK(account_info.has_value()); + CHECK_EQ(hosted_domain, account_info->hosted_domain); + // |PrimaryAccountManager::SetAuthenticatedAccountId| simply ignores the call // if there is already a signed in user. Check that there is no signed in // account or that the new signed in account matches the old one to avoid a // mismatch between the old and the new authenticated accounts. - if (!old_authenticated_account_id.empty()) - CHECK_EQ(new_authenticated_account_id, old_authenticated_account_id); + if (!identity_manager_->HasPrimaryAccount()) { + DCHECK(identity_manager_->GetPrimaryAccountMutator()); + const bool success = + identity_manager_->GetPrimaryAccountMutator()->SetPrimaryAccount( + account_id); + CHECK(success); + } - // Update the PrimaryAccountManager with the new logged in identity. - auto* account_mutator = identity_manager_->GetPrimaryAccountMutator(); - DCHECK(account_mutator); - account_mutator->SetPrimaryAccount(new_authenticated_account_id); - - // Reload all credentials to match the desktop model. Exclude all the - // accounts ids that are the primary account ids on other profiles. - // TODO(crbug.com/930094): Eliminate this. - identity_manager_->LegacyReloadAccountsFromSystem(); - StoreAccountsInPrefs(); + // The primary account should now be set to the expected account_id. + CHECK_EQ(account_id, identity_manager_->GetPrimaryAccountId()); // Kick-off sync: The authentication error UI (sign in infobar and warning // badge in settings screen) check the sync auth error state. Sync
diff --git a/ios/chrome/browser/signin/authentication_service_unittest.mm b/ios/chrome/browser/signin/authentication_service_unittest.mm index f26adc8..42711f4 100644 --- a/ios/chrome/browser/signin/authentication_service_unittest.mm +++ b/ios/chrome/browser/signin/authentication_service_unittest.mm
@@ -252,7 +252,7 @@ TEST_F(AuthenticationServiceTest, TestSignInAndGetAuthenticatedIdentity) { // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_NSEQ(identity_, authentication_service_->GetAuthenticatedIdentity()); @@ -287,7 +287,7 @@ // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup()) .WillOnce(Return(true)); @@ -309,7 +309,7 @@ // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup()) .WillOnce(Invoke( @@ -335,7 +335,7 @@ // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_CALL(*sync_setup_service_mock_, HasFinishedInitialSetup()) .WillOnce(Return(false)); @@ -352,7 +352,7 @@ TEST_F(AuthenticationServiceTest, TestHandleForgottenIdentityNoPromptSignIn) { // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); // Set the authentication service as "In Foreground", remove identity and run // the loop. @@ -371,7 +371,7 @@ TEST_F(AuthenticationServiceTest, TestHandleForgottenIdentityPromptSignIn) { // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); // Set the authentication service as "In Background", remove identity and run // the loop. @@ -393,7 +393,7 @@ // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); // Store the accounts and get them back from the prefs. They should be the // same as the token service accounts. @@ -421,7 +421,7 @@ OnApplicationEnterForegroundReloadCredentials) { // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); identity_service_->AddIdentities(@[ @"foo3" ]); @@ -483,7 +483,7 @@ TEST_F(AuthenticationServiceTest, HaveAccountsNotChanged) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); identity_service_->AddIdentities(@[ @"foo3" ]); FireIdentityListChanged(); @@ -498,7 +498,7 @@ TEST_F(AuthenticationServiceTest, HaveAccountsChanged) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); identity_service_->AddIdentities(@[ @"foo3" ]); FireIdentityListChanged(); @@ -515,7 +515,7 @@ TEST_F(AuthenticationServiceTest, HaveAccountsChangedBackground) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); identity_service_->AddIdentities(@[ @"foo3" ]); FireIdentityListChanged(); @@ -533,7 +533,7 @@ TEST_F(AuthenticationServiceTest, IsAuthenticatedBackground) { // Sign in. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_TRUE(authentication_service_->IsAuthenticated()); // Remove the signed in identity while in background, and check that @@ -562,7 +562,7 @@ // Sign in user emails as account ids. SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); std::vector<std::string> accounts_in_prefs = GetAccountsInPrefs(); ASSERT_EQ(2U, accounts_in_prefs.size()); EXPECT_EQ("foo2@foo.com", accounts_in_prefs[0]); @@ -599,7 +599,7 @@ // refresh token available notifications. TEST_F(AuthenticationServiceTest, MDMErrorsClearedOnForeground) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); EXPECT_EQ(2, refresh_token_available_count_); NSDictionary* user_info = [NSDictionary dictionary]; @@ -626,7 +626,7 @@ // refresh token available notifications. TEST_F(AuthenticationServiceTest, MDMErrorsClearedOnSignout) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); NSDictionary* user_info = [NSDictionary dictionary]; SetCachedMDMInfo(identity_, user_info); @@ -643,7 +643,7 @@ // to MDM service when necessary. TEST_F(AuthenticationServiceTest, HandleMDMNotification) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount( @@ -679,7 +679,7 @@ // the primary account is blocked. TEST_F(AuthenticationServiceTest, HandleMDMBlockedNotification) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount( @@ -737,7 +737,7 @@ // corresponding error for the account. TEST_F(AuthenticationServiceTest, ShowMDMErrorDialog) { SetExpectationsForSignIn(); - authentication_service_->SignIn(identity_, std::string()); + authentication_service_->SignIn(identity_, kNoHostedDomainFound); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount(
diff --git a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h index 8f573fb..e90fbbfd 100644 --- a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h +++ b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h
@@ -8,7 +8,6 @@ #import <UIKit/UIKit.h> @protocol BrowserCommands; -@protocol ActivityServicePresentation; // Activity that sends the tab to another of the user's devices. @interface SendTabToSelfActivity : UIActivity @@ -17,14 +16,8 @@ + (NSString*)activityIdentifier; // Initialize the send tab to self activity with the |dispatcher| that is used -// to add the tab to the other device, |sendTabToSelfTargets| is the list of -// devices that will be presented to the user via |presenter| and |title| -// represents the title of the tab being shared. -- (instancetype)initWithDispatcher:(id<BrowserCommands>)dispatcher - sendTabToSelfTargets: - (NSDictionary<NSString*, NSString*>*)sendTabToSelfTargets - presenter:(id<ActivityServicePresentation>)presenter - title:(NSString*)title; +// to add the tab to the other device. +- (instancetype)initWithDispatcher:(id<BrowserCommands>)dispatcher; @end
diff --git a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.mm b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.mm index 614218ef..17a008b 100644 --- a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.mm +++ b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.mm
@@ -4,16 +4,10 @@ #import "ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h" -#include "base/ios/block_types.h" -#include "base/logging.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" -#include "base/strings/sys_string_conversions.h" -#import "ios/chrome/browser/ui/activity_services/requirements/activity_service_presentation.h" #import "ios/chrome/browser/ui/commands/browser_commands.h" -#import "ios/chrome/browser/ui/commands/send_tab_to_self_command.h" -#import "ios/chrome/browser/ui/context_menu/context_menu_item.h" #include "ios/chrome/grit/ios_strings.h" #include "ui/base/l10n/l10n_util_mac.h" @@ -27,7 +21,6 @@ @"com.google.chrome.sendTabToSelfActivity"; const char kClickResultHistogramName[] = "SendTabToSelf.ShareMenu.ClickResult"; -const char kDeviceCountHistogramName[] = "SendTabToSelf.ShareMenu.DeviceCount"; // TODO(crbug.com/970886): Move to a directory accessible on all platforms. // State of the send tab to self option in the context menu. @@ -46,15 +39,6 @@ // The dispatcher that handles when the activity is performed. @property(nonatomic, weak, readonly) id<BrowserCommands> dispatcher; -// The dictionary of target devices and their cache guids. -@property(nonatomic, strong, readonly) - NSDictionary<NSString*, NSString*>* sendTabToSelfTargets; - -// The presenter that will present the action sheet to show devices. -@property(nonatomic, weak, readonly) id<ActivityServicePresentation> presenter; - -// The title of the shared tab. -@property(nonatomic, copy, readonly) NSString* title; @end @implementation SendTabToSelfActivity @@ -63,18 +47,11 @@ return kSendTabToSelfActivityType; } -- (instancetype)initWithDispatcher:(id<BrowserCommands>)dispatcher - sendTabToSelfTargets: - (NSDictionary<NSString*, NSString*>*)sendTabToSelfTargets - presenter:(id<ActivityServicePresentation>)presenter - title:(NSString*)title { +- (instancetype)initWithDispatcher:(id<BrowserCommands>)dispatcher { base::UmaHistogramEnumeration(kClickResultHistogramName, SendTabToSelfClickResult::kShowItem); if (self = [super init]) { - _sendTabToSelfTargets = sendTabToSelfTargets; _dispatcher = dispatcher; - _presenter = presenter; - _title = [title copy]; } return self; } @@ -102,40 +79,7 @@ } - (void)performActivity { - // TODO(crbug.com/970284) once the modal dialog is created this will be - // removed. - BOOL useContextMenu = YES; - if (useContextMenu) { - NSMutableArray<ContextMenuItem*>* targetActions = - [NSMutableArray arrayWithCapacity:[_sendTabToSelfTargets count]]; - - for (NSString* key in _sendTabToSelfTargets) { - NSString* deviceID = _sendTabToSelfTargets[key]; - // Retain |self| here since a |weakSelf| would be deallocated when - // displaying the target device sheet, as the ActivitySheet will be gone. - ProceduralBlock action = ^{ - SendTabToSelfCommand* command = - [[SendTabToSelfCommand alloc] initWithTargetDeviceID:deviceID]; - base::UmaHistogramEnumeration(kClickResultHistogramName, - SendTabToSelfClickResult::kClickItem); - [self.dispatcher sendTabToSelf:command]; - }; - [targetActions addObject:[[ContextMenuItem alloc] initWithTitle:key - action:action]]; - } - - base::UmaHistogramEnumeration(kClickResultHistogramName, - SendTabToSelfClickResult::kShowDeviceList); - base::UmaHistogramCounts100(kDeviceCountHistogramName, - [targetActions count]); - - NSString* title = l10n_util::GetNSStringF( - IDS_IOS_SHARE_MENU_SEND_TAB_TO_SELF_DEVICE_ACTION, - base::SysNSStringToUTF16(_title)); - [_presenter showActivityServiceContextMenu:title items:targetActions]; - } else { - [self.dispatcher showSendTabToSelfUI]; - } + [self.dispatcher showSendTabToSelfUI]; [self activityDidFinish:YES]; }
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm index 78193c8..8a5d2f1 100644 --- a/ios/chrome/browser/ui/activity_services/activity_service_controller.mm +++ b/ios/chrome/browser/ui/activity_services/activity_service_controller.mm
@@ -72,13 +72,11 @@ // share to the sharing activities. - (NSArray*)activityItemsForData:(ShareToData*)data; // Returns an array of UIActivity objects that can handle the given |data|. -- (NSArray*) - applicationActivitiesForData:(ShareToData*)data - dispatcher:(id<BrowserCommands>)dispatcher - bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel - canSendTabToSelf:(BOOL)canSendTabToSelf - sendTabToSelfModel: - (send_tab_to_self::SendTabToSelfModel*)sendTabToSelfModel; +- (NSArray*)applicationActivitiesForData:(ShareToData*)data + dispatcher:(id<BrowserCommands>)dispatcher + bookmarkModel: + (bookmarks::BookmarkModel*)bookmarkModel + canSendTabToSelf:(BOOL)canSendTabToSelf; // Processes |extensionItems| returned from App Extension invocation returning // the |activityType|. Calls shareDelegate_ with the processed returned items // and |result| of activity. Returns whether caller should reset UI. @@ -159,23 +157,14 @@ BOOL canSendTabToSelf = send_tab_to_self::ShouldOfferFeature(browserState, data.shareURL); - send_tab_to_self::SendTabToSelfModel* sendTabToSelfModel = nil; - send_tab_to_self::SendTabToSelfSyncService* syncService = - SendTabToSelfSyncServiceFactory::GetForBrowserState(browserState); - // Users in incognito mode do not have a sync service set. - if (syncService) { - sendTabToSelfModel = syncService->GetSendTabToSelfModel(); - } - DCHECK(!activityViewController_); activityViewController_ = [[UIActivityViewController alloc] initWithActivityItems:[self activityItemsForData:data] - applicationActivities: - [self applicationActivitiesForData:data - dispatcher:dispatcher - bookmarkModel:bookmarkModel - canSendTabToSelf:canSendTabToSelf - sendTabToSelfModel:sendTabToSelfModel]]; + applicationActivities:[self + applicationActivitiesForData:data + dispatcher:dispatcher + bookmarkModel:bookmarkModel + canSendTabToSelf:canSendTabToSelf]]; // Reading List and Print activities refer to iOS' version of these. // Chrome-specific implementations of these two activities are provided @@ -287,43 +276,20 @@ return [NSString stringWithFormat:@"%@ \u2022 %@", device_name, active_time]; } -- (NSArray*) - applicationActivitiesForData:(ShareToData*)data - dispatcher:(id<BrowserCommands>)dispatcher - bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel - canSendTabToSelf:(BOOL)canSendTabToSelf - sendTabToSelfModel: - (send_tab_to_self::SendTabToSelfModel*)sendTabToSelfModel { +- (NSArray*)applicationActivitiesForData:(ShareToData*)data + dispatcher:(id<BrowserCommands>)dispatcher + bookmarkModel: + (bookmarks::BookmarkModel*)bookmarkModel + canSendTabToSelf:(BOOL)canSendTabToSelf { NSMutableArray* applicationActivities = [NSMutableArray array]; [applicationActivities addObject:[[CopyActivity alloc] initWithURL:data.shareURL]]; if (data.shareURL.SchemeIsHTTPOrHTTPS()) { - if (canSendTabToSelf && sendTabToSelfModel) { - std::map<std::string, send_tab_to_self::TargetDeviceInfo> - target_device_map = - sendTabToSelfModel->GetTargetDeviceNameToCacheInfoMap(); - NSMutableDictionary* sendTabToSelfTargets = - [[NSMutableDictionary alloc] init]; - for (auto const& iter : target_device_map) { - int daysSinceLastUpdate = - (base::Time::Now() - iter.second.last_updated_timestamp).InDays(); - NSString* title = [self - sendTabToSelfContextMenuTitleForDevice:base::SysUTF8ToNSString( - iter.first) - daysSinceLastUpdate:daysSinceLastUpdate]; - - NSString* cache_guid = base::SysUTF8ToNSString(iter.second.cache_guid); - sendTabToSelfTargets[title] = cache_guid; - } - + if (canSendTabToSelf) { SendTabToSelfActivity* sendTabToSelfActivity = - [[SendTabToSelfActivity alloc] - initWithDispatcher:dispatcher - sendTabToSelfTargets:sendTabToSelfTargets - presenter:presentationProvider_ - title:data.title]; + [[SendTabToSelfActivity alloc] initWithDispatcher:dispatcher]; [applicationActivities addObject:sendTabToSelfActivity]; }
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm index 12c6be0..6b7664a 100644 --- a/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm +++ b/ios/chrome/browser/ui/activity_services/activity_service_controller_unittest.mm
@@ -13,13 +13,10 @@ #include "components/bookmarks/browser/bookmark_node.h" #include "components/bookmarks/test/bookmark_test_helpers.h" #include "components/send_tab_to_self/features.h" -#include "components/send_tab_to_self/send_tab_to_self_model.h" -#include "components/send_tab_to_self/send_tab_to_self_sync_service.h" #include "components/sync/driver/sync_driver_switches.h" #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h" #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" #import "ios/chrome/browser/passwords/password_form_filler.h" -#include "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h" #import "ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h" #import "ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h" #import "ios/chrome/browser/ui/activity_services/activities/print_activity.h" @@ -78,13 +75,11 @@ @interface ActivityServiceController (CrVisibleForTesting) - (NSArray*)activityItemsForData:(ShareToData*)data; -- (NSArray*) - applicationActivitiesForData:(ShareToData*)data - dispatcher:(id<BrowserCommands>)dispatcher - bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel - canSendTabToSelf:(BOOL)canSendTabToSelf - sendTabToSelfModel: - (send_tab_to_self::SendTabToSelfModel*)sendTabToSelfModel; +- (NSArray*)applicationActivitiesForData:(ShareToData*)data + dispatcher:(id<BrowserCommands>)dispatcher + bookmarkModel: + (bookmarks::BookmarkModel*)bookmarkModel + canSendTabToSelf:(BOOL)canSendTabToSelf; - (BOOL)processItemsReturnedFromActivity:(NSString*)activityType status:(ShareTo::ShareResult)result @@ -193,10 +188,6 @@ bookmark_model_ = ios::BookmarkModelFactory::GetForBrowserState( chrome_browser_state_.get()); bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_); - send_tab_to_self_model_ = - SendTabToSelfSyncServiceFactory::GetForBrowserState( - chrome_browser_state_.get()) - ->GetSendTabToSelfModel(); parentController_ = [[UIViewController alloc] initWithNibName:nil bundle:nil]; [[UIApplication sharedApplication] keyWindow].rootViewController = @@ -328,7 +319,6 @@ ShareToData* shareData_; std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; bookmarks::BookmarkModel* bookmark_model_; - send_tab_to_self::SendTabToSelfModel* send_tab_to_self_model_; }; TEST_F(ActivityServiceControllerTest, PresentAndDismissController) { @@ -548,8 +538,7 @@ [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:false]; ASSERT_EQ(5U, [items count]); EXPECT_TRUE(ArrayContainsObjectOfClass(items, [PrintActivity class])); @@ -563,12 +552,10 @@ isPageSearchable:YES userAgent:web::UserAgentType::NONE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:nil + bookmarkModel:bookmark_model_ + canSendTabToSelf:false]; EXPECT_EQ(4U, [items count]); EXPECT_FALSE(ArrayContainsObjectOfClass(items, [PrintActivity class])); } @@ -594,8 +581,7 @@ [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:false]; ASSERT_EQ(6U, [items count]); // Verify non-HTTP URL. @@ -607,12 +593,10 @@ isPageSearchable:YES userAgent:web::UserAgentType::MOBILE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:nil + bookmarkModel:bookmark_model_ + canSendTabToSelf:false]; ASSERT_EQ(2U, [items count]); } @@ -636,8 +620,7 @@ [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:false]; ASSERT_EQ(5U, [items count]); UIActivity* activity = [items objectAtIndex:2]; EXPECT_EQ([BookmarkActivity class], [activity class]); @@ -660,12 +643,10 @@ isPageSearchable:YES userAgent:web::UserAgentType::NONE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:nil + bookmarkModel:bookmark_model_ + canSendTabToSelf:false]; ASSERT_EQ(5U, [items count]); activity = [items objectAtIndex:2]; EXPECT_EQ([BookmarkActivity class], [activity class]); @@ -698,8 +679,7 @@ [activityController applicationActivitiesForData:data dispatcher:mockDispatcher bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:false]; ASSERT_EQ(6U, [items count]); UIActivity* activity = [items objectAtIndex:4]; EXPECT_EQ([RequestDesktopOrMobileSiteActivity class], [activity class]); @@ -721,12 +701,10 @@ thumbnailGenerator:DummyThumbnailGeneratorBlock()]; mockDispatcher = OCMProtocolMock(@protocol(BrowserCommands)); OCMExpect([mockDispatcher requestMobileSite]); - items = - [activityController applicationActivitiesForData:data - dispatcher:mockDispatcher - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:mockDispatcher + bookmarkModel:bookmark_model_ + canSendTabToSelf:false]; ASSERT_EQ(6U, [items count]); activity = [items objectAtIndex:4]; EXPECT_EQ([RequestDesktopOrMobileSiteActivity class], [activity class]); @@ -844,8 +822,7 @@ [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:false]; ASSERT_EQ(5U, [items count]); EXPECT_TRUE(ArrayContainsObjectOfClass(items, [FindInPageActivity class])); @@ -859,12 +836,10 @@ isPageSearchable:NO userAgent:web::UserAgentType::NONE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:nil + bookmarkModel:bookmark_model_ + canSendTabToSelf:false]; EXPECT_EQ(4U, [items count]); EXPECT_FALSE(ArrayContainsObjectOfClass(items, [FindInPageActivity class])); } @@ -890,8 +865,7 @@ [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:true - sendTabToSelfModel:send_tab_to_self_model_]; + canSendTabToSelf:true]; ASSERT_EQ(6U, [items count]); EXPECT_TRUE(ArrayContainsObjectOfClass(items, [SendTabToSelfActivity class])); @@ -910,32 +884,10 @@ userAgent:web::UserAgentType::NONE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:false - sendTabToSelfModel:send_tab_to_self_model_]; - ASSERT_EQ(5U, [items count]); - EXPECT_FALSE( - ArrayContainsObjectOfClass(items, [SendTabToSelfActivity class])); - - // Verify searchable data with no send tab to self model. - data = [[ShareToData alloc] - initWithShareURL:GURL("https://chromium.org/printable") - visibleURL:GURL("https://chromium.org/printable") - title:@"bar" - isOriginalTitle:YES - isPagePrintable:YES - isPageSearchable:YES - userAgent:web::UserAgentType::NONE - thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = [activityController applicationActivitiesForData:data dispatcher:nil bookmarkModel:bookmark_model_ - canSendTabToSelf:true - sendTabToSelfModel:nil]; + canSendTabToSelf:false]; ASSERT_EQ(5U, [items count]); EXPECT_FALSE( ArrayContainsObjectOfClass(items, [SendTabToSelfActivity class])); @@ -949,12 +901,10 @@ isPageSearchable:YES userAgent:web::UserAgentType::NONE thumbnailGenerator:DummyThumbnailGeneratorBlock()]; - items = - [activityController applicationActivitiesForData:data - dispatcher:nil - bookmarkModel:bookmark_model_ - canSendTabToSelf:true - sendTabToSelfModel:send_tab_to_self_model_]; + items = [activityController applicationActivitiesForData:data + dispatcher:nil + bookmarkModel:bookmark_model_ + canSendTabToSelf:true]; EXPECT_EQ(2U, [items count]); EXPECT_FALSE( ArrayContainsObjectOfClass(items, [SendTabToSelfActivity class]));
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn index 5a9cd96..6da87518 100644 --- a/ios/chrome/browser/ui/authentication/BUILD.gn +++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -63,6 +63,7 @@ "//ios/chrome/browser/ui/util", "//ios/chrome/browser/unified_consent", "//ios/chrome/common", + "//ios/chrome/common/colors", "//ios/chrome/common/ui_util", "//ios/public/provider/chrome/browser", "//ios/public/provider/chrome/browser/images",
diff --git a/ios/chrome/browser/ui/authentication/authentication_constants.h b/ios/chrome/browser/ui/authentication/authentication_constants.h index 35f657f..18a223e0 100644 --- a/ios/chrome/browser/ui/authentication/authentication_constants.h +++ b/ios/chrome/browser/ui/authentication/authentication_constants.h
@@ -19,19 +19,11 @@ // Vertical margin between the header image and the main title. extern const CGFloat kAuthenticationHeaderTitleMargin; -// Alpha for the title color. -extern const CGFloat kAuthenticationTitleColorAlpha; -// Alpha for the text color. -extern const CGFloat kAuthenticationTextColorAlpha; - // Alpha for the separator color. extern const CGFloat kAuthenticationSeparatorColorAlpha; // Height of the separator. extern const CGFloat kAuthenticationSeparatorHeight; -// Color of the checkmark. -extern const int kAuthenticationCheckmarkColor; - // Header image name. extern NSString* const kAuthenticationHeaderImageName;
diff --git a/ios/chrome/browser/ui/authentication/authentication_constants.mm b/ios/chrome/browser/ui/authentication/authentication_constants.mm index 76d23a5..7acb5fa 100644 --- a/ios/chrome/browser/ui/authentication/authentication_constants.mm +++ b/ios/chrome/browser/ui/authentication/authentication_constants.mm
@@ -16,12 +16,7 @@ const CGFloat kAuthenticationHorizontalMargin = 16.; const CGFloat kAuthenticationHeaderTitleMargin = 19.; -const CGFloat kAuthenticationTitleColorAlpha = 0.87; -const CGFloat kAuthenticationTextColorAlpha = 0.54; - const CGFloat kAuthenticationSeparatorColorAlpha = 0.12; const CGFloat kAuthenticationSeparatorHeight = 1; -const int kAuthenticationCheckmarkColor = 0x1A73E8; - NSString* const kAuthenticationHeaderImageName = @"unified_consent_header";
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm index c12fdec9..05b571c 100644 --- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm +++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -177,7 +177,9 @@ withHostedDomain:(NSString*)hostedDomain toBrowserState:(ios::ChromeBrowserState*)browserState { AuthenticationServiceFactory::GetForBrowserState(browserState) - ->SignIn(identity, base::SysNSStringToUTF8(hostedDomain)); + ->SignIn(identity, [hostedDomain length] > 0 + ? base::SysNSStringToUTF8(hostedDomain) + : kNoHostedDomainFound); } - (void)signOutBrowserState:(ios::ChromeBrowserState*)browserState {
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm b/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm index f990c4a..562ace8b 100644 --- a/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm +++ b/ios/chrome/browser/ui/authentication/authentication_flow_unittest.mm
@@ -166,7 +166,7 @@ toBrowserState:browser_state_.get()]; AuthenticationServiceFactory::GetForBrowserState(browser_state_.get()) - ->SignIn(identity1_, std::string()); + ->SignIn(identity1_, kNoHostedDomainFound); [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; CheckSignInCompletion(true); @@ -211,7 +211,7 @@ [[performer_ expect] commitSyncForBrowserState:browser_state_.get()]; AuthenticationServiceFactory::GetForBrowserState(browser_state_.get()) - ->SignIn(identity2_, std::string()); + ->SignIn(identity2_, kNoHostedDomainFound); [authentication_flow_ startSignInWithCompletion:sign_in_completion_]; CheckSignInCompletion(true);
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h index 756a381..51cee1a 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h
@@ -102,8 +102,6 @@ @property(nonatomic, readonly) ios::ChromeBrowserState* browserState; -@property(nonatomic, readonly) UIColor* backgroundColor; - // Vertical padding used underneath buttons. Default value is 18. @property(nonatomic, assign) CGFloat buttonVerticalPadding;
diff --git a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm index d2ef1bd..8bb1a12 100644 --- a/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm +++ b/ios/chrome/browser/ui/authentication/chrome_signin_view_controller.mm
@@ -39,13 +39,14 @@ #include "ios/chrome/browser/ui/authentication/signin_account_selector_view_controller.h" #include "ios/chrome/browser/ui/authentication/signin_confirmation_view_controller.h" #include "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_coordinator.h" -#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/util/label_link_controller.h" #import "ios/chrome/browser/ui/util/rtl_geometry.h" #import "ios/chrome/browser/ui/util/ui_util.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" #include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h" +#import "ios/chrome/common/colors/UIColor+cr_semantic_colors.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #include "ios/chrome/common/string_util.h" #include "ios/chrome/grit/ios_chromium_strings.h" #include "ios/chrome/grit/ios_strings.h" @@ -144,7 +145,6 @@ SigninAccountSelectorViewControllerDelegate, UnifiedConsentCoordinatorDelegate> @property(nonatomic, strong) ChromeIdentity* selectedIdentity; - @end @implementation ChromeSigninViewController { @@ -340,20 +340,21 @@ } - (void)setPrimaryButtonStyling:(MDCButton*)button { - [button setBackgroundColor:[[MDCPalette cr_bluePalette] tint500] + [button setBackgroundColor:[UIColor colorNamed:kTintColor] forState:UIControlStateNormal]; [button setImage:nil forState:UIControlStateNormal]; - [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - [button setUnderlyingColorHint:[UIColor blackColor]]; + [button setTitleColor:[UIColor colorNamed:kSolidButtonTextColor] + forState:UIControlStateNormal]; + [button setUnderlyingColorHint:UIColor.cr_systemBackgroundColor]; [button setInkColor:[UIColor colorWithWhite:1 alpha:0.2f]]; } - (void)setSecondaryButtonStyling:(MDCButton*)button { - [button setBackgroundColor:self.backgroundColor + [button setBackgroundColor:UIColor.cr_systemBackgroundColor forState:UIControlStateNormal]; - [button setTitleColor:[[MDCPalette cr_bluePalette] tint500] + [button setTitleColor:[UIColor colorNamed:kTintColor] forState:UIControlStateNormal]; - [button setUnderlyingColorHint:[UIColor whiteColor]]; + [button setUnderlyingColorHint:UIColor.cr_systemBackgroundColor]; [button setInkColor:[UIColor colorWithWhite:0 alpha:0.06f]]; } @@ -461,6 +462,13 @@ setCenter:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))]; } +- (void)updateGradientColors { + _gradientLayer.colors = @[ + (id)[UIColor.cr_systemBackgroundColor colorWithAlphaComponent:0].CGColor, + (id)UIColor.cr_systemBackgroundColor.CGColor + ]; +} + #pragma mark - Accessibility - (BOOL)accessibilityPerformEscape { @@ -479,11 +487,6 @@ return _delegate; } -- (UIColor*)backgroundColor { - return _unifiedConsentEnabled ? [UIColor whiteColor] - : [[MDCPalette greyPalette] tint50]; -} - - (NSString*)identityPickerTitle { return l10n_util::GetNSString(IDS_IOS_ACCOUNT_CONSISTENCY_SETUP_TITLE); } @@ -923,7 +926,7 @@ - (void)viewDidLoad { [super viewDidLoad]; - self.view.backgroundColor = self.backgroundColor; + self.view.backgroundColor = UIColor.cr_systemBackgroundColor; _primaryButton = [[MDCFlatButton alloc] init]; [self setPrimaryButtonStyling:_primaryButton]; @@ -947,17 +950,14 @@ [[MDCActivityIndicator alloc] initWithFrame:CGRectZero]; [_activityIndicator setDelegate:self]; [_activityIndicator setStrokeWidth:3]; - [_activityIndicator - setCycleColors:@[ [[MDCPalette cr_bluePalette] tint500] ]]; + [_activityIndicator setCycleColors:@[ [UIColor colorNamed:kTintColor] ]]; [self.view addSubview:_activityIndicator]; } _gradientView = [[UIView alloc] initWithFrame:CGRectZero]; _gradientLayer = [CAGradientLayer layer]; [_gradientView setUserInteractionEnabled:NO]; - _gradientLayer.colors = [NSArray - arrayWithObjects:(id)[[UIColor colorWithWhite:1 alpha:0] CGColor], - (id)[self.backgroundColor CGColor], nil]; + [self updateGradientColors]; [[_gradientView layer] insertSublayer:_gradientLayer atIndex:0]; [self.view addSubview:_gradientView]; } @@ -980,6 +980,23 @@ [self updateLayout]; } +- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; +#if defined(__IPHONE_13_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + if (@available(iOS 13, *)) { + if ([self.traitCollection + hasDifferentColorAppearanceComparedToTraitCollection: + previousTraitCollection]) { + [self updateGradientColors]; + // As of iOS 13 Beta 3, MDCFlatButton doesn't update it's colors + // automatically. These lines do it instead. + [self updatePrimaryButtonForIdentityPickerState]; + [self setSecondaryButtonStyling:_secondaryButton]; + } + } +#endif +} + #pragma mark - Events - (void)onPrimaryButtonPressed:(id)sender {
diff --git a/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm b/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm index c512d3c1..d947c8b 100644 --- a/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm +++ b/ios/chrome/browser/ui/authentication/re_signin_infobar_delegate_unittest.mm
@@ -59,7 +59,7 @@ AuthenticationService* authentication_service = AuthenticationServiceFactory::GetForBrowserState( chrome_browser_state_.get()); - authentication_service->SignIn(chrome_identity, std::string()); + authentication_service->SignIn(chrome_identity, kNoHostedDomainFound); } web::TestWebThreadBundle thread_bundle_;
diff --git a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm index 0fdbaaea..ac77ce2 100644 --- a/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/authentication/signed_in_accounts_view_controller_unittest.mm
@@ -41,7 +41,7 @@ @[ @"identity1", @"identity2", @"identity3" ]); auth_service_->SignIn( [identity_service->GetAllIdentitiesSortedForDisplay() objectAtIndex:0], - std::string()); + kNoHostedDomainFound); } protected:
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/BUILD.gn b/ios/chrome/browser/ui/authentication/unified_consent/BUILD.gn index 61cfa0a5d..8b05a5c 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent/BUILD.gn +++ b/ios/chrome/browser/ui/authentication/unified_consent/BUILD.gn
@@ -42,7 +42,6 @@ "//ios/chrome/browser/ui/authentication/unified_consent/identity_chooser:identity_chooser_ui", "//ios/chrome/browser/ui/colors", "//ios/chrome/browser/ui/util", - "//ios/chrome/browser/ui/util", "//ios/chrome/common", "//ios/chrome/common/colors", "//ios/chrome/common/ui_util",
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm index 899f14b..c140f82 100644 --- a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm +++ b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
@@ -13,6 +13,7 @@ #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" #import "ios/chrome/browser/ui/util/label_link_controller.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" +#import "ios/chrome/common/colors/UIColor+cr_semantic_colors.h" #include "ios/chrome/common/string_util.h" #import "ios/chrome/common/ui_util/constraints_ui_util.h" #include "ios/chrome/grit/ios_chromium_strings.h" @@ -168,7 +169,7 @@ UILabel* title = [self addLabelWithStringId:IDS_IOS_ACCOUNT_UNIFIED_CONSENT_TITLE fontStyle:kAuthenticationTitleFontStyle - textColorAlpha:kAuthenticationTitleColorAlpha + textColor:UIColor.cr_labelColor parentView:container]; // Identity picker view. @@ -184,20 +185,19 @@ UILabel* syncTitleLabel = [self addLabelWithStringId:IDS_IOS_ACCOUNT_UNIFIED_CONSENT_SYNC_TITLE fontStyle:kAuthenticationTextFontStyle - textColorAlpha:kAuthenticationTitleColorAlpha + textColor:UIColor.cr_labelColor parentView:container]; UILabel* syncSubtitleLabel = [self addLabelWithStringId:IDS_IOS_ACCOUNT_UNIFIED_CONSENT_SYNC_SUBTITLE fontStyle:kAuthenticationTextFontStyle - textColorAlpha:kAuthenticationTextColorAlpha + textColor:UIColor.cr_secondaryLabelColor parentView:container]; // Separator. UIView* separator = [[UIView alloc] initWithFrame:CGRectZero]; separator.translatesAutoresizingMaskIntoConstraints = NO; - separator.backgroundColor = - [UIColor colorWithWhite:0 alpha:kAuthenticationSeparatorColorAlpha]; + separator.backgroundColor = UIColor.cr_secondarySystemBackgroundColor; [container addSubview:separator]; // Customize label. @@ -205,7 +205,7 @@ self.customizeSyncLabel = [self addLabelWithStringId:self.openSettingsStringId fontStyle:kAuthenticationTextFontStyle - textColorAlpha:kAuthenticationTextColorAlpha + textColor:UIColor.cr_secondaryLabelColor parentView:container]; // Layouts @@ -356,14 +356,14 @@ // Adds label with title |stringId| into |parentView|. - (UILabel*)addLabelWithStringId:(int)stringId fontStyle:(UIFontTextStyle)fontStyle - textColorAlpha:(CGFloat)textColorAlpha + textColor:(UIColor*)textColor parentView:(UIView*)parentView { DCHECK(stringId); DCHECK(parentView); UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero]; label.translatesAutoresizingMaskIntoConstraints = NO; label.font = [UIFont preferredFontForTextStyle:fontStyle]; - label.textColor = [UIColor colorWithWhite:0 alpha:textColorAlpha]; + label.textColor = textColor; label.text = l10n_util::GetNSString(stringId); _consentStringIds.push_back(stringId); label.numberOfLines = 0;
diff --git a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm index 7138858..5b290a469 100644 --- a/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm +++ b/ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.mm
@@ -28,7 +28,7 @@ const CGFloat kTappedBannerViewScale = 0.98; const CGFloat kSelectedBannerViewScale = 1.02; const CGFloat kSelectBannerAnimationDurationInSeconds = 0.2; -const CGFloat kTappedBannerAnimationDurationInSeconds = 0.1; +const CGFloat kTappedBannerAnimationDurationInSeconds = 0.05; const CGFloat kSelectedBannerViewYShadowOffset = 8.0; // Bottom Grip constants. @@ -390,6 +390,9 @@ // state. After the animation it presentd the Infobar Modal. - (void)animateBannerTappedAndPresentModal { [self.interactionDelegate infobarBannerStartedInteraction]; + // TODO(crbug.com/961343): Interrupt this animation in case the Banner needs + // to be dismissed mid tap (Currently it will be dismmissed after the + // animation). [UIView animateWithDuration:kTappedBannerAnimationDurationInSeconds animations:^{ self.view.superview.transform = CGAffineTransformMakeScale(
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm index 20554b2e..471b9d6 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_confirm_coordinator.mm
@@ -73,11 +73,12 @@ #pragma mark - InfobarCoordinatorImplementation -- (void)configureModalViewController { +- (BOOL)configureModalViewController { self.modalViewController = [[InfobarModalViewController alloc] initWithModalDelegate:self]; self.modalViewController.title = base::SysUTF16ToNSString(self.confirmInfobarDelegate->GetMessageText()); + return YES; } - (void)infobarBannerWasPresented {
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm index 604521e..f1f7de6b 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -140,9 +140,12 @@ self.modalTransitionDriver = [[InfobarModalTransitionDriver alloc] initWithTransitionMode:InfobarModalTransitionBase]; self.modalTransitionDriver.modalPositioner = self; + __weak __typeof(self) weakSelf = self; [self presentInfobarModalFrom:self.baseViewController - driver:self.modalTransitionDriver]; - [self infobarModalPresentedFromBanner:NO]; + driver:self.modalTransitionDriver + completion:^{ + [weakSelf infobarModalPresentedFromBanner:NO]; + }]; }; // Dismiss InfobarBanner first if being presented. @@ -193,9 +196,12 @@ self.modalTransitionDriver = [[InfobarModalTransitionDriver alloc] initWithTransitionMode:InfobarModalTransitionBanner]; self.modalTransitionDriver.modalPositioner = self; + __weak __typeof(self) weakSelf = self; [self presentInfobarModalFrom:self.bannerViewController - driver:self.modalTransitionDriver]; - [self infobarModalPresentedFromBanner:YES]; + driver:self.modalTransitionDriver + completion:^{ + [weakSelf infobarModalPresentedFromBanner:YES]; + }]; } - (void)dismissInfobarBanner:(id)sender @@ -327,8 +333,9 @@ #pragma mark InfobarCoordinatorImplementation -- (void)configureModalViewController { +- (BOOL)configureModalViewController { NOTREACHED() << "Subclass must implement."; + return NO; } - (void)infobarBannerWasPresented { @@ -358,9 +365,19 @@ #pragma mark - Private +// |presentingViewController| presents the InfobarModal using |driver|. If +// Modal is presented successfully |completion| will be executed. - (void)presentInfobarModalFrom:(UIViewController*)presentingViewController - driver:(InfobarModalTransitionDriver*)driver { - [self configureModalViewController]; + driver:(InfobarModalTransitionDriver*)driver + completion:(ProceduralBlock)completion { + BOOL infobarWasConfigured = [self configureModalViewController]; + if (!infobarWasConfigured) { + if (driver.transitionMode == InfobarModalTransitionBanner) { + [self dismissInfobarBannerAnimated:NO completion:nil]; + } + return; + } + DCHECK(self.modalViewController); UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:self.modalViewController]; @@ -369,7 +386,7 @@ self.modalNavigationController = navController; [presentingViewController presentViewController:navController animated:YES - completion:nil]; + completion:completion]; [self.badgeDelegate infobarModalWasPresented:self.infobarType]; }
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h index df8031f..41a5d9ef 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator_implementation.h
@@ -13,8 +13,9 @@ @protocol InfobarCoordinatorImplementation // Initializes and configures the ModalViewController that will be presented by -// the InfobarCoordinator. -- (void)configureModalViewController; +// the InfobarCoordinator. Returns YES if the modalViewController was configured +// successfully. If it returns NO no Modal should be presented. +- (BOOL)configureModalViewController; // Performs any actions related to an Infobar Banner presentation. - (void)infobarBannerWasPresented;
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm index 98cf2bc..fb03489 100644 --- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm +++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -102,7 +102,13 @@ #pragma mark - InfobarCoordinatorImplementation -- (void)configureModalViewController { +- (BOOL)configureModalViewController { + // Return early if there's no delegate. e.g. A Modal presentation has been + // triggered after the Infobar was destroyed, but before the badge/banner + // were dismissed. + if (!self.passwordInfoBarDelegate) + return NO; + // Do not use |self.infobarBannerType| since the modal type might change each // time is presented. e.g. We present a Modal of type Save and tap on "Save". // The next time the Modal is presented we'll present a Modal of Type "Update" @@ -137,6 +143,7 @@ self.passwordInfoBarDelegate->IsCurrentPasswordSaved(); [self recordModalPresentationMetricsUsingModalType:infobarModalType]; + return YES; } - (void)infobarBannerWasPresented {
diff --git a/ios/chrome/browser/ui/settings/passphrase_table_view_controller_test.mm b/ios/chrome/browser/ui/settings/passphrase_table_view_controller_test.mm index 6c01bae..8263608 100644 --- a/ios/chrome/browser/ui/settings/passphrase_table_view_controller_test.mm +++ b/ios/chrome/browser/ui/settings/passphrase_table_view_controller_test.mm
@@ -92,7 +92,7 @@ ChromeIdentity* identity = [identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:0]; AuthenticationServiceFactory::GetForBrowserState(chrome_browser_state_.get()) - ->SignIn(identity, ""); + ->SignIn(identity, kNoHostedDomainFound); } void PassphraseTableViewControllerTest::SetUpNavigationController(
diff --git a/ios/chrome/common/colors/resources/BUILD.gn b/ios/chrome/common/colors/resources/BUILD.gn index 0e373a8..bb15786f 100644 --- a/ios/chrome/common/colors/resources/BUILD.gn +++ b/ios/chrome/common/colors/resources/BUILD.gn
@@ -7,6 +7,7 @@ group("resources") { deps = [ ":destructive_tint_color", + ":disabled_tint_color", ":solid_button_text_color", ":tint_color", ] @@ -18,6 +19,12 @@ ] } +colorset("disabled_tint_color") { + sources = [ + "disabled_tint_color.colorset/Contents.json", + ] +} + colorset("solid_button_text_color") { sources = [ "solid_button_text_color.colorset/Contents.json",
diff --git a/ios/chrome/common/colors/resources/disabled_tint_color.colorset/Contents.json b/ios/chrome/common/colors/resources/disabled_tint_color.colorset/Contents.json new file mode 100644 index 0000000..e80d39e --- /dev/null +++ b/ios/chrome/common/colors/resources/disabled_tint_color.colorset/Contents.json
@@ -0,0 +1,38 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "display-p3", + "components" : { + "red" : "0xA0", + "alpha" : "1.000", + "blue" : "0xA0", + "green" : "0xA0" + } + } + }, + { + "idiom" : "universal", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "red" : "0x70", + "alpha" : "1.000", + "blue" : "0x70", + "green" : "0x70" + } + } + } + ] +} \ No newline at end of file
diff --git a/ios/chrome/common/colors/semantic_color_names.h b/ios/chrome/common/colors/semantic_color_names.h index b9168a0..bb36ec4 100644 --- a/ios/chrome/common/colors/semantic_color_names.h +++ b/ios/chrome/common/colors/semantic_color_names.h
@@ -8,6 +8,7 @@ #import <UIKit/UIKit.h> extern NSString* const kDestructiveTintColor; +extern NSString* const kDisabledTintColor; extern NSString* const kSolidButtonTextColor; extern NSString* const kTintColor;
diff --git a/ios/chrome/common/colors/semantic_color_names.mm b/ios/chrome/common/colors/semantic_color_names.mm index 46d5ebc..57d6a5a 100644 --- a/ios/chrome/common/colors/semantic_color_names.mm +++ b/ios/chrome/common/colors/semantic_color_names.mm
@@ -9,5 +9,6 @@ #endif NSString* const kDestructiveTintColor = @"destructive_tint_color"; +NSString* const kDisabledTintColor = @"disabled_tint_color"; NSString* const kSolidButtonTextColor = @"solid_button_text_color"; NSString* const kTintColor = @"tint_color";
diff --git a/ios/public/provider/chrome/browser/signin/BUILD.gn b/ios/public/provider/chrome/browser/signin/BUILD.gn index a931d2c..1d076df 100644 --- a/ios/public/provider/chrome/browser/signin/BUILD.gn +++ b/ios/public/provider/chrome/browser/signin/BUILD.gn
@@ -38,6 +38,7 @@ deps = [ ":signin", "//base", + "//components/signin/core/browser:shared", "//google_apis", "//ios/public/provider/chrome/browser", "//ui/base:test_support",
diff --git a/ios/public/provider/chrome/browser/signin/DEPS b/ios/public/provider/chrome/browser/signin/DEPS index eb0f651b..a285d454 100644 --- a/ios/public/provider/chrome/browser/signin/DEPS +++ b/ios/public/provider/chrome/browser/signin/DEPS
@@ -2,4 +2,7 @@ "test_signin_resources_provider\.mm": [ "+ui/base/test/ios/ui_image_test_utils.h", ], + "fake_chrome_identity\.mm": [ + "+components/signin/core/browser/account_info.h" + ] }
diff --git a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm index ed108d9..262c981 100644 --- a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm +++ b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm
@@ -8,6 +8,8 @@ #error "This file requires ARC support." #endif +#include "components/signin/core/browser/account_info.h" + @implementation FakeChromeIdentity { NSString* _userEmail; NSString* _gaiaID; @@ -51,4 +53,8 @@ return _hashedGaiaID; } +- (NSString*)hostedDomain { + return @(kNoHostedDomainFound); +} + @end
diff --git a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm index 843095f..bd717db 100644 --- a/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm +++ b/ios/web_view/internal/autofill/cwv_credit_card_verifier_unittest.mm
@@ -198,7 +198,7 @@ id unused_data_source = OCMProtocolMock(@protocol(CWVCreditCardVerifierDataSource)); NSString* cvc = @"123"; - BOOL store_locally = YES; + BOOL store_locally = NO; [credit_card_verifier_ loadRiskData:std::move(base::DoNothing())]; [credit_card_verifier_ verifyWithCVC:cvc @@ -207,7 +207,7 @@ storeLocally:store_locally dataSource:unused_data_source delegate:nil]; - EXPECT_TRUE(credit_card_verifier_.lastStoreLocallyValue); + EXPECT_FALSE(credit_card_verifier_.lastStoreLocallyValue); const FakeCardUnmaskDelegate::UnmaskResponse& unmask_response_ = card_unmask_delegate_.GetUnmaskResponse();
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm index 20af6f24..76e60e0 100644 --- a/ios/web_view/internal/sync/cwv_sync_controller.mm +++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -158,15 +158,18 @@ _dataSource = dataSource; _currentIdentity = identity; - AccountInfo info; - info.gaia = base::SysNSStringToUTF8(identity.gaiaID); - info.email = base::SysNSStringToUTF8(identity.email); - std::string newAuthenticatedAccountID = - _identityManager->LegacySeedAccountInfo(info); - auto* primaryAccountMutator = _identityManager->GetPrimaryAccountMutator(); - primaryAccountMutator->SetPrimaryAccount(newAuthenticatedAccountID); + DCHECK(_dataSource); + DCHECK(_currentIdentity); - [self reloadCredentials]; + const CoreAccountId accountId = _identityManager->PickAccountIdForAccount( + base::SysNSStringToUTF8(identity.gaiaID), + base::SysNSStringToUTF8(identity.email)); + + _identityManager->LegacyReloadAccountsFromSystem(); + CHECK(_identityManager->HasAccountWithRefreshToken(accountId)); + + _identityManager->GetPrimaryAccountMutator()->SetPrimaryAccount(accountId); + CHECK_EQ(_identityManager->GetPrimaryAccountId(), accountId); } - (void)stopSyncAndClearIdentity {
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 2faee6a..c500e5c9 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -112,6 +112,8 @@ "android/codec_wrapper.h", "android/device_info.cc", "android/device_info.h", + "android/direct_shared_image_video_provider.cc", + "android/direct_shared_image_video_provider.h", "android/image_reader_gl_owner.cc", "android/image_reader_gl_owner.h", "android/maybe_render_early_manager.cc",
diff --git a/media/gpu/android/codec_wrapper.cc b/media/gpu/android/codec_wrapper.cc index 90eccf28..74181bd 100644 --- a/media/gpu/android/codec_wrapper.cc +++ b/media/gpu/android/codec_wrapper.cc
@@ -107,14 +107,20 @@ CodecOutputBuffer::CodecOutputBuffer(scoped_refptr<CodecWrapperImpl> codec, int64_t id, - gfx::Size size) + const gfx::Size& size) : codec_(std::move(codec)), id_(id), size_(size) {} +// For testing. +CodecOutputBuffer::CodecOutputBuffer(int64_t id, const gfx::Size& size) + : id_(id), size_(size) {} + CodecOutputBuffer::~CodecOutputBuffer() { // While it will work if we re-release the buffer, since CodecWrapper handles // it properly, we can save a lock + (possibly) post by checking here if we // know that it has been rendered already. - if (!was_rendered_) + // + // |codec_| might be null, but only for tests. + if (!was_rendered_ && codec_) codec_->ReleaseCodecOutputBuffer(id_, false); }
diff --git a/media/gpu/android/codec_wrapper.h b/media/gpu/android/codec_wrapper.h index 4ac14173..b0de73ee5 100644 --- a/media/gpu/android/codec_wrapper.h +++ b/media/gpu/android/codec_wrapper.h
@@ -22,6 +22,7 @@ #include "media/gpu/media_gpu_export.h" namespace media { +class CodecWrapper; class CodecWrapperImpl; using CodecSurfacePair = std::pair<std::unique_ptr<MediaCodecBridge>, @@ -43,12 +44,24 @@ // The size of the image. gfx::Size size() const { return size_; } + // Note that you can't use the first ctor, since CodecWrapperImpl isn't + // defined here. Use the second, and it'll be nullptr. + template <typename... Args> + static std::unique_ptr<CodecOutputBuffer> CreateForTesting(Args&&... args) { + // std::make_unique can't access the constructor. + return std::unique_ptr<CodecOutputBuffer>( + new CodecOutputBuffer(std::forward<Args>(args)...)); + } + private: // Let CodecWrapperImpl call the constructor. friend class CodecWrapperImpl; CodecOutputBuffer(scoped_refptr<CodecWrapperImpl> codec, int64_t id, - gfx::Size size); + const gfx::Size& size); + + // For testing, since CodecWrapperImpl isn't available. Uses nullptr. + CodecOutputBuffer(int64_t id, const gfx::Size& size); scoped_refptr<CodecWrapperImpl> codec_; int64_t id_;
diff --git a/media/gpu/android/direct_shared_image_video_provider.cc b/media/gpu/android/direct_shared_image_video_provider.cc new file mode 100644 index 0000000..c430ba8 --- /dev/null +++ b/media/gpu/android/direct_shared_image_video_provider.cc
@@ -0,0 +1,292 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/gpu/android/direct_shared_image_video_provider.h" + +#include <memory> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram_functions.h" +#include "base/metrics/histogram_macros.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/task_runner_util.h" +#include "gpu/command_buffer/service/abstract_texture.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/shared_image_factory.h" +#include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/ipc/service/command_buffer_stub.h" +#include "gpu/ipc/service/gpu_channel.h" +#include "gpu/ipc/service/gpu_channel_manager.h" +#include "media/base/bind_to_current_loop.h" +#include "media/base/media_switches.h" +#include "media/gpu/android/shared_image_video.h" +#include "mojo/public/cpp/bindings/callback_helpers.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/scoped_make_current.h" + +namespace media { +namespace { + +bool MakeContextCurrent(gpu::CommandBufferStub* stub) { + return stub && stub->decoder_context()->MakeCurrent(); +} + +scoped_refptr<gpu::SharedContextState> GetSharedContext( + gpu::CommandBufferStub* stub, + gpu::ContextResult* result) { + auto shared_context = + stub->channel()->gpu_channel_manager()->GetSharedContextState(result); + return (*result == gpu::ContextResult::kSuccess) ? shared_context : nullptr; +} + +void ContextStateResultUMA(gpu::ContextResult result) { + base::UmaHistogramEnumeration( + "Media.GpuSharedImageVideoFactory.SharedContextStateResult", result); +} + +} // namespace + +using gpu::gles2::AbstractTexture; + +DirectSharedImageVideoProvider::DirectSharedImageVideoProvider( + scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, + GetStubCB get_stub_cb) + : gpu_factory_(gpu_task_runner, std::move(get_stub_cb)), + gpu_task_runner_(std::move(gpu_task_runner)) {} + +DirectSharedImageVideoProvider::~DirectSharedImageVideoProvider() = default; + +// TODO(liberato): add a thread hop to create the default texture owner, but +// not as part of this class. just post something from VideoFrameFactory. +void DirectSharedImageVideoProvider::Initialize(GpuInitCB gpu_init_cb) { + // Note that we do not BindToCurrentLoop |gpu_init_cb|, since it is supposed + // to be called on the gpu main thread, which is somewhat hacky. + gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::Initialize, + std::move(gpu_init_cb)); +} + +void DirectSharedImageVideoProvider::RequestImage( + ImageReadyCB cb, + const ImageSpec& spec, + std::unique_ptr<CodecOutputBuffer> output_buffer, + scoped_refptr<TextureOwner> texture_owner, + PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb) { + auto image_ready_cb = BindToCurrentLoop( + base::BindOnce(&DirectSharedImageVideoProvider::OnImageReady, + std::move(cb), std::move(output_buffer), texture_owner, + std::move(promotion_hint_cb), gpu_task_runner_)); + + // It's unclear that we should handle the image group, but since CodecImages + // have to be registered on it, we do. If the CodecImage is ever re-used, + // then part of that re-use would be to call the (then mis-named) + // destruction cb to remove it from the group. + // + // Also note that CodecImage shouldn't be the thing that's added to the + // group anyway. The thing that owns buffer management is all we really + // care about, and that doesn't have anything to do with GLImage. + + gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::CreateImage, + std::move(image_ready_cb), spec, std::move(texture_owner)); +} + +// static +void DirectSharedImageVideoProvider::OnImageReady( + ImageReadyCB cb, + std::unique_ptr<CodecOutputBuffer> output_buffer, + scoped_refptr<TextureOwner> texture_owner, + PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb, + scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, + ImageRecord record) { + // It's okay to access |codec_image| here, since it's not used anywhere. + // We could let |cb| do this, but, in the long term, we want our caller to + // provide the output buffer / etc. to us, so we can hide how we use it. + record.codec_image_holder->codec_image_raw()->Initialize( + std::move(output_buffer), std::move(texture_owner), + std::move(promotion_hint_cb)); + + std::move(cb).Run(std::move(record)); +} + +GpuSharedImageVideoFactory::GpuSharedImageVideoFactory( + SharedImageVideoProvider::GetStubCB get_stub_cb) + : weak_factory_(this) { + DETACH_FROM_THREAD(thread_checker_); + stub_ = get_stub_cb.Run(); + if (stub_) + stub_->AddDestructionObserver(this); +} + +GpuSharedImageVideoFactory::~GpuSharedImageVideoFactory() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (stub_) + stub_->RemoveDestructionObserver(this); +} + +void GpuSharedImageVideoFactory::Initialize( + SharedImageVideoProvider::GpuInitCB gpu_init_cb) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!MakeContextCurrent(stub_)) { + std::move(gpu_init_cb).Run(nullptr); + return; + } + + decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context()); + + gpu::ContextResult result; + auto shared_context = GetSharedContext(stub_, &result); + if (!shared_context) { + DLOG(ERROR) + << "GpuSharedImageVideoFactory: Unable to get a shared context."; + ContextStateResultUMA(result); + std::move(gpu_init_cb).Run(nullptr); + return; + } + + // Make the shared context current. + auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>( + shared_context->context(), shared_context->surface()); + if (!shared_context->IsCurrent(nullptr)) { + result = gpu::ContextResult::kTransientFailure; + DLOG(ERROR) + << "GpuSharedImageVideoFactory: Unable to make shared context current."; + ContextStateResultUMA(result); + std::move(gpu_init_cb).Run(nullptr); + return; + } + + // Note that if |gpu_init_cb| posts, then the ScopedMakeCurrent won't help. + std::move(gpu_init_cb).Run(std::move(shared_context)); +} + +void GpuSharedImageVideoFactory::CreateImage( + FactoryImageReadyCB image_ready_cb, + const SharedImageVideoProvider::ImageSpec& spec, + scoped_refptr<TextureOwner> texture_owner) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + // Generate a shared image mailbox. + auto mailbox = gpu::Mailbox::GenerateForSharedImage(); + auto codec_image = base::MakeRefCounted<CodecImage>(); + + TRACE_EVENT0("media", "GpuSharedImageVideoFactory::CreateVideoFrame"); + + if (!CreateImageInternal(spec, std::move(texture_owner), mailbox, + codec_image)) { + return; + } + + // This callback destroys the shared image when video frame is + // released/destroyed. This callback has a weak pointer to the shared image + // stub because shared image stub could be destroyed before video frame. In + // those cases there is no need to destroy the shared image as the shared + // image stub destruction will cause all the shared images to be destroyed. + auto destroy_shared_image = + stub_->channel()->shared_image_stub()->GetSharedImageDestructionCallback( + mailbox); + + // Guarantee that the SharedImage is destroyed even if the VideoFrame is + // dropped. Otherwise we could keep shared images we don't need alive. + auto release_cb = mojo::WrapCallbackWithDefaultInvokeIfNotRun( + BindToCurrentLoop(std::move(destroy_shared_image)), gpu::SyncToken()); + + SharedImageVideoProvider::ImageRecord record; + record.mailbox = mailbox; + record.release_cb = std::move(release_cb); + record.ycbcr_info = ycbcr_info_; + // Since |codec_image|'s ref holders can be destroyed by stub destruction, we + // create a ref to it for the MaybeRenderEarlyManager. This is a hack; we + // should not be sending the CodecImage at all. The MaybeRenderEarlyManager + // should work with some other object that happens to be used by CodecImage, + // and non-GL things, to hold the output buffer, etc. + record.codec_image_holder = base::MakeRefCounted<CodecImageHolder>( + base::SequencedTaskRunnerHandle::Get(), std::move(codec_image)); + + std::move(image_ready_cb).Run(std::move(record)); +} + +bool GpuSharedImageVideoFactory::CreateImageInternal( + const SharedImageVideoProvider::ImageSpec& spec, + scoped_refptr<TextureOwner> texture_owner, + gpu::Mailbox mailbox, + scoped_refptr<CodecImage> image) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!MakeContextCurrent(stub_)) + return false; + + gpu::gles2::ContextGroup* group = stub_->decoder_context()->GetContextGroup(); + if (!group) + return false; + gpu::gles2::TextureManager* texture_manager = group->texture_manager(); + if (!texture_manager) + return false; + + const auto& size = spec.size; + + // Create a Texture and a CodecImage to back it. + // TODO(liberato): Once legacy mailbox support is removed, we don't need to + // create this texture. So, we won't need |texture_owner| either. + std::unique_ptr<AbstractTexture> texture = decoder_helper_->CreateTexture( + GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size.width(), size.height(), GL_RGBA, + GL_UNSIGNED_BYTE); + + // Attach the image to the texture. + // Either way, we expect this to be UNBOUND (i.e., decoder-managed). For + // overlays, BindTexImage will return true, causing it to transition to the + // BOUND state, and thus receive ScheduleOverlayPlane calls. For TextureOwner + // backed images, BindTexImage will return false, and CopyTexImage will be + // tried next. + // TODO(liberato): consider not binding this as a StreamTextureImage if we're + // using an overlay. There's no advantage. We'd likely want to create (and + // initialize to a 1x1 texture) a 2D texture above in that case, in case + // somebody tries to sample from it. Be sure that promotion hints still + // work properly, though -- they might require a stream texture image. + GLuint texture_owner_service_id = + texture_owner ? texture_owner->GetTextureId() : 0; + texture->BindStreamTextureImage(image.get(), texture_owner_service_id); + + gpu::ContextResult result; + auto shared_context = GetSharedContext(stub_, &result); + if (!shared_context) { + DLOG(ERROR) + << "GpuSharedImageVideoFactory: Unable to get a shared context."; + ContextStateResultUMA(result); + return false; + } + + // Create a shared image. + // TODO(vikassoni): Hardcoding colorspace to SRGB. Figure how if media has a + // colorspace and wire it here. + // TODO(vikassoni): This shared image need to be thread safe eventually for + // webview to work with shared images. + auto shared_image = std::make_unique<SharedImageVideo>( + mailbox, gfx::ColorSpace::CreateSRGB(), std::move(image), + std::move(texture), std::move(shared_context), + false /* is_thread_safe */); + + if (!ycbcr_info_) + ycbcr_info_ = shared_image->GetYcbcrInfo(); + + // Register it with shared image mailbox as well as legacy mailbox. This + // keeps |shared_image| around until its destruction cb is called. + // NOTE: Currently none of the video mailbox consumer uses shared image + // mailbox. + DCHECK(stub_->channel()->gpu_channel_manager()->shared_image_manager()); + stub_->channel()->shared_image_stub()->factory()->RegisterBacking( + std::move(shared_image), /* legacy_mailbox */ true); + + return true; +} + +void GpuSharedImageVideoFactory::OnWillDestroyStub(bool have_context) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(stub_); + stub_ = nullptr; + decoder_helper_ = nullptr; +} + +} // namespace media
diff --git a/media/gpu/android/direct_shared_image_video_provider.h b/media/gpu/android/direct_shared_image_video_provider.h new file mode 100644 index 0000000..b7ccb48 --- /dev/null +++ b/media/gpu/android/direct_shared_image_video_provider.h
@@ -0,0 +1,123 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_GPU_ANDROID_DIRECT_SHARED_IMAGE_VIDEO_PROVIDER_H_ +#define MEDIA_GPU_ANDROID_DIRECT_SHARED_IMAGE_VIDEO_PROVIDER_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/sequence_bound.h" +#include "gpu/command_buffer/service/gles2_cmd_decoder.h" +#include "gpu/command_buffer/service/shared_image_representation.h" +#include "gpu/command_buffer/service/texture_manager.h" +#include "gpu/ipc/common/vulkan_ycbcr_info.h" +#include "gpu/ipc/service/command_buffer_stub.h" +#include "media/base/video_frame.h" +#include "media/gpu/android/codec_image.h" +#include "media/gpu/android/maybe_render_early_manager.h" +#include "media/gpu/android/shared_image_video_provider.h" +#include "media/gpu/android/surface_texture_gl_owner.h" +#include "media/gpu/android/video_frame_factory.h" +#include "media/gpu/gles2_decoder_helper.h" +#include "media/gpu/media_gpu_export.h" +#include "ui/gl/gl_bindings.h" + +namespace media { +class GpuSharedImageVideoFactory; + +// SharedImageVideoProvider implementation that lives on the thread that it's +// created on, but hops to the GPU thread to create new shared images on demand. +class MEDIA_GPU_EXPORT DirectSharedImageVideoProvider + : public SharedImageVideoProvider { + public: + DirectSharedImageVideoProvider( + scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, + GetStubCB get_stub_cb); + ~DirectSharedImageVideoProvider() override; + + // SharedImageVideoProvider + void Initialize(GpuInitCB get_stub_cb) override; + void RequestImage(ImageReadyCB cb, + const ImageSpec& spec, + std::unique_ptr<CodecOutputBuffer> output_buffer, + scoped_refptr<TextureOwner> texture_owner, + PromotionHintAggregator::NotifyPromotionHintCB + promotion_hint_cb) override; + + private: + static void OnImageReady( + ImageReadyCB cb, + std::unique_ptr<CodecOutputBuffer> output_buffer, + scoped_refptr<TextureOwner> texture_owner, + PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb, + scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, + ImageRecord record); + + base::SequenceBound<GpuSharedImageVideoFactory> gpu_factory_; + + scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(DirectSharedImageVideoProvider); +}; + +// GpuSharedImageVideoFactory creates SharedImageVideo objects. It must be run +// on the gpu main thread. +// +// GpuSharedImageVideoFactory is an implementation detail of +// DirectSharedImageVideoProvider. It's here since we'll likely re-use it for +// the pool. +class GpuSharedImageVideoFactory + : public gpu::CommandBufferStub::DestructionObserver { + public: + explicit GpuSharedImageVideoFactory( + SharedImageVideoProvider::GetStubCB get_stub_cb); + ~GpuSharedImageVideoFactory() override; + + // Will run |init_cb| with the shared context current. |init_cb| should not + // post, else the context won't be current. + void Initialize(SharedImageVideoProvider::GpuInitCB init_cb); + + // Similar to SharedImageVideoProvider::ImageReadyCB, but provides additional + // details for the provider that's using us. + using FactoryImageReadyCB = SharedImageVideoProvider::ImageReadyCB; + + // Creates a SharedImage for |spec|, and returns it via the callback. + // TODO(liberato): |texture_owner| is only needed to get the service id, to + // create the per-frame texture. All of that is only needed for legacy + // mailbox support, where we have to have one texture per CodecImage. + void CreateImage(FactoryImageReadyCB cb, + const SharedImageVideoProvider::ImageSpec& spec, + scoped_refptr<TextureOwner> texture_owner); + + private: + // Creates a SharedImage for |mailbox|, and returns success or failure. + bool CreateImageInternal(const SharedImageVideoProvider::ImageSpec& spec, + scoped_refptr<TextureOwner> texture_owner, + gpu::Mailbox mailbox, + scoped_refptr<CodecImage> image); + + void OnWillDestroyStub(bool have_context) override; + + gpu::CommandBufferStub* stub_ = nullptr; + + // A helper for creating textures. Only valid while |stub_| is valid. + std::unique_ptr<GLES2DecoderHelper> decoder_helper_; + + // Sampler conversion information which is used in vulkan context. This is + // constant for all the frames in a video and hence we cache it. + base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_; + + THREAD_CHECKER(thread_checker_); + + base::WeakPtrFactory<GpuSharedImageVideoFactory> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(GpuSharedImageVideoFactory); +}; + +} // namespace media + +#endif // MEDIA_GPU_ANDROID_DIRECT_SHARED_IMAGE_VIDEO_PROVIDER_H_
diff --git a/media/gpu/android/shared_image_video_provider.h b/media/gpu/android/shared_image_video_provider.h index 69a9eed..fc9ec38 100644 --- a/media/gpu/android/shared_image_video_provider.h +++ b/media/gpu/android/shared_image_video_provider.h
@@ -14,6 +14,8 @@ #include "ui/gfx/geometry/size.h" namespace gpu { +class CommandBufferStub; +class SharedContextState; struct SyncToken; } // namespace gpu @@ -22,6 +24,10 @@ // Provider class for shared images. class MEDIA_GPU_EXPORT SharedImageVideoProvider { public: + using GetStubCB = base::RepeatingCallback<gpu::CommandBufferStub*()>; + using GpuInitCB = + base::OnceCallback<void(scoped_refptr<gpu::SharedContextState>)>; + // Description of the underlying properties of the shared image. struct ImageSpec { ImageSpec(const gfx::Size& size); @@ -66,6 +72,12 @@ using ImageReadyCB = base::OnceCallback<void(ImageRecord)>; + // Initialize this provider. On success, |gpu_init_cb| will be run with the + // SharedContextState (and the context current), on the gpu main thread. This + // is mostly a hack to allow VideoFrameFactoryImpl to create a TextureOwner on + // the right context. Will call |gpu_init_cb| with nullptr otherwise. + virtual void Initialize(GpuInitCB gpu_init_cb) = 0; + // Call |cb| when we have a shared image that matches |spec|. We may call // |cb| back before returning, or we might post it for later. // |output_buffer|, |texture_owner|, and |promotion_hint_cb| probably should
diff --git a/media/gpu/android/video_frame_factory.h b/media/gpu/android/video_frame_factory.h index b341ae4..ee5e80a 100644 --- a/media/gpu/android/video_frame_factory.h +++ b/media/gpu/android/video_frame_factory.h
@@ -15,10 +15,6 @@ #include "media/gpu/media_gpu_export.h" #include "ui/gfx/geometry/size.h" -namespace gpu { -class CommandBufferStub; -} // namespace gpu - namespace media { class CodecOutputBuffer; @@ -30,7 +26,6 @@ // safe. Virtual for testing; see VideoFrameFactoryImpl. class MEDIA_GPU_EXPORT VideoFrameFactory { public: - using GetStubCb = base::Callback<gpu::CommandBufferStub*()>; using InitCb = base::RepeatingCallback<void(scoped_refptr<TextureOwner>)>; using OnceOutputCb = base::OnceCallback<void(scoped_refptr<VideoFrame>)>;
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc index a6e92a72..d3814ff 100644 --- a/media/gpu/android/video_frame_factory_impl.cc +++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -12,21 +12,11 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/task_runner_util.h" -#include "base/threading/sequence_bound.h" -#include "gpu/command_buffer/common/constants.h" -#include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "gpu/command_buffer/service/memory_tracking.h" -#include "gpu/command_buffer/service/shared_image_factory.h" -#include "gpu/command_buffer/service/texture_manager.h" -#include "gpu/ipc/service/command_buffer_stub.h" -#include "gpu/ipc/service/gpu_channel.h" -#include "gpu/ipc/service/gpu_channel_manager.h" +#include "base/trace_event/trace_event.h" +#include "gpu/command_buffer/service/abstract_texture.h" #include "media/base/bind_to_current_loop.h" #include "media/base/media_switches.h" #include "media/base/video_frame.h" @@ -37,17 +27,11 @@ #include "media/gpu/android/shared_image_video.h" #include "media/gpu/command_buffer_helper.h" #include "mojo/public/cpp/bindings/callback_helpers.h" -#include "ui/gl/android/surface_texture.h" -#include "ui/gl/gl_bindings.h" #include "ui/gl/scoped_make_current.h" namespace media { namespace { -bool MakeContextCurrent(gpu::CommandBufferStub* stub) { - return stub && stub->decoder_context()->MakeCurrent(); -} - TextureOwner::Mode GetTextureOwnerMode( VideoFrameFactory::OverlayMode overlay_mode) { const bool a_image_reader_supported = @@ -72,100 +56,33 @@ return TextureOwner::Mode::kSurfaceTextureInsecure; } -scoped_refptr<gpu::SharedContextState> GetSharedContext( - gpu::CommandBufferStub* stub, - gpu::ContextResult* result) { - auto shared_context = - stub->channel()->gpu_channel_manager()->GetSharedContextState(result); - if (*result != gpu::ContextResult::kSuccess) - return nullptr; - return shared_context; -} +// Run on the GPU main thread to allocate the texture owner, and return it +// via |init_cb|. +static void AllocateTextureOwnerOnGpuThread( + VideoFrameFactory::InitCb init_cb, + VideoFrameFactory::OverlayMode overlay_mode, + scoped_refptr<gpu::SharedContextState> shared_context_state) { + if (!shared_context_state) { + std::move(init_cb).Run(nullptr); + return; + } -void ContextStateResultUMA(gpu::ContextResult result) { - UMA_HISTOGRAM_ENUMERATION( - "Media.GpuSharedImageVideoFactory.SharedContextStateResult", result); + std::move(init_cb).Run( + TextureOwner::Create(TextureOwner::CreateTexture(shared_context_state), + GetTextureOwnerMode(overlay_mode))); } } // namespace using gpu::gles2::AbstractTexture; -// TODO(liberato): This should be in its own file. However, for now, it's here -// so that we don't have to move GpuSharedImageFactory with it, since the latter -// is an implementation detail. This makes the diffs much easier to review. -// Moving it can be done separately with no other code changes. -class DirectSharedImageVideoProvider : public SharedImageVideoProvider { - public: - DirectSharedImageVideoProvider( - scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - VideoFrameFactory::OverlayMode overlay_mode, - VideoFrameFactory::GetStubCb get_stub_cb, - VideoFrameFactory::InitCb init_cb) - : gpu_factory_(gpu_task_runner), gpu_task_runner_(gpu_task_runner) { - gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::Initialize, - overlay_mode, std::move(get_stub_cb), - BindToCurrentLoop(std::move(init_cb))); - } - - // TODO(liberato): add a thread hop to create the default texture owner, but - // not as part of this class. just post something from VideoFrameFactory. - void Initialize(VideoFrameFactory::OverlayMode overlay_mode, - VideoFrameFactory::GetStubCb get_stub_cb) {} - - void RequestImage(ImageReadyCB cb, - const ImageSpec& spec, - std::unique_ptr<CodecOutputBuffer> output_buffer, - scoped_refptr<TextureOwner> texture_owner, - PromotionHintAggregator::NotifyPromotionHintCB - promotion_hint_cb) override { - auto image_ready_cb = BindToCurrentLoop( - base::BindOnce(&DirectSharedImageVideoProvider::OnImageReady, - std::move(cb), std::move(output_buffer), texture_owner, - std::move(promotion_hint_cb), gpu_task_runner_)); - - // It's unclear that we should handle the image group, but since CodecImages - // have to be registered on it, we do. If the CodecImage is ever re-used, - // then part of that re-use would be to call the (then mis-named) - // destruction cb to remove it from the group. - // - // Also note that CodecImage shouldn't be the thing that's added to the - // group anyway. The thing that owns buffer management is all we really - // care about, and that doesn't have anything to do with GLImage. - - gpu_factory_.Post(FROM_HERE, &GpuSharedImageVideoFactory::CreateImage, - std::move(image_ready_cb), spec, - std::move(texture_owner)); - } - - static void OnImageReady( - ImageReadyCB cb, - std::unique_ptr<CodecOutputBuffer> output_buffer, - scoped_refptr<TextureOwner> texture_owner, - PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb, - scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - ImageRecord record) { - // It's okay to access |codec_image| here, since it's not used anywhere. - // We could let |cb| do this, but, in the long term, we want our caller to - // provide the output buffer / etc. to us, so we can hide how we use it. - record.codec_image_holder->codec_image_raw()->Initialize( - std::move(output_buffer), std::move(texture_owner), - std::move(promotion_hint_cb)); - - std::move(cb).Run(std::move(record)); - } - - base::SequenceBound<GpuSharedImageVideoFactory> gpu_factory_; - scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; -}; - VideoFrameFactoryImpl::VideoFrameFactoryImpl( scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetStubCb get_stub_cb, const gpu::GpuPreferences& gpu_preferences, + std::unique_ptr<SharedImageVideoProvider> image_provider, std::unique_ptr<MaybeRenderEarlyManager> mre_manager) - : gpu_task_runner_(std::move(gpu_task_runner)), - get_stub_cb_(std::move(get_stub_cb)), + : image_provider_(std::move(image_provider)), + gpu_task_runner_(std::move(gpu_task_runner)), enable_threaded_texture_mailboxes_( gpu_preferences.enable_threaded_texture_mailboxes), mre_manager_(std::move(mre_manager)), @@ -178,10 +95,13 @@ void VideoFrameFactoryImpl::Initialize(OverlayMode overlay_mode, InitCb init_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!image_provider_); overlay_mode_ = overlay_mode; - image_provider_ = std::make_unique<DirectSharedImageVideoProvider>( - gpu_task_runner_, overlay_mode, get_stub_cb_, std::move(init_cb)); + // On init success, create the TextureOwner and hop it back to this thread to + // call |init_cb|. + auto gpu_init_cb = + base::BindOnce(&AllocateTextureOwnerOnGpuThread, + BindToCurrentLoop(std::move(init_cb)), overlay_mode); + image_provider_->Initialize(std::move(gpu_init_cb)); } void VideoFrameFactoryImpl::SetSurfaceBundle( @@ -341,181 +261,11 @@ base::OnceClosure closure) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Hop through |gpu_task_runner_| to ensure it comes after pending frames. + // TODO(liberato): If we're using a pool for SharedImageVideo, then this + // doesn't really make much sense. SharedImageVideoProvider should do this + // for us instead. gpu_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(), std::move(closure)); } -GpuSharedImageVideoFactory::GpuSharedImageVideoFactory() : weak_factory_(this) { - DETACH_FROM_THREAD(thread_checker_); -} - -GpuSharedImageVideoFactory::~GpuSharedImageVideoFactory() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (stub_) - stub_->RemoveDestructionObserver(this); -} - -void GpuSharedImageVideoFactory::Initialize( - VideoFrameFactoryImpl::OverlayMode overlay_mode, - VideoFrameFactoryImpl::GetStubCb get_stub_cb, - VideoFrameFactory::InitCb init_cb) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - stub_ = get_stub_cb.Run(); - if (!MakeContextCurrent(stub_)) { - std::move(init_cb).Run(nullptr); - return; - } - stub_->AddDestructionObserver(this); - - decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context()); - - gpu::ContextResult result; - auto shared_context = GetSharedContext(stub_, &result); - if (!shared_context) { - LOG(ERROR) << "GpuSharedImageVideoFactory: Unable to get a shared context."; - ContextStateResultUMA(result); - std::move(init_cb).Run(nullptr); - return; - } - - // Make the shared context current. - auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>( - shared_context->context(), shared_context->surface()); - if (!shared_context->IsCurrent(nullptr)) { - result = gpu::ContextResult::kTransientFailure; - LOG(ERROR) - << "GpuSharedImageVideoFactory: Unable to make shared context current."; - ContextStateResultUMA(result); - std::move(init_cb).Run(nullptr); - return; - } - std::move(init_cb).Run( - TextureOwner::Create(TextureOwner::CreateTexture(shared_context), - GetTextureOwnerMode(overlay_mode))); -} - -void GpuSharedImageVideoFactory::CreateImage( - FactoryImageReadyCB image_ready_cb, - const SharedImageVideoProvider::ImageSpec& spec, - scoped_refptr<TextureOwner> texture_owner) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - // Generate a shared image mailbox. - auto mailbox = gpu::Mailbox::GenerateForSharedImage(); - auto codec_image = base::MakeRefCounted<CodecImage>(); - - bool success = - CreateImageInternal(spec, std::move(texture_owner), mailbox, codec_image); - TRACE_EVENT0("media", "GpuSharedImageVideoFactory::CreateVideoFrame"); - if (!success) - return; - - // This callback destroys the shared image when video frame is - // released/destroyed. This callback has a weak pointer to the shared image - // stub because shared image stub could be destroyed before video frame. In - // those cases there is no need to destroy the shared image as the shared - // image stub destruction will cause all the shared images to be destroyed. - auto destroy_shared_image = - stub_->channel()->shared_image_stub()->GetSharedImageDestructionCallback( - mailbox); - - // Guarantee that the SharedImage is destroyed even if the VideoFrame is - // dropped. Otherwise we could keep shared images we don't need alive. - auto release_cb = mojo::WrapCallbackWithDefaultInvokeIfNotRun( - BindToCurrentLoop(std::move(destroy_shared_image)), gpu::SyncToken()); - - SharedImageVideoProvider::ImageRecord record; - record.mailbox = mailbox; - record.release_cb = std::move(release_cb); - record.ycbcr_info = ycbcr_info_; - // Since |codec_image|'s ref holders can be destroyed by stub destruction, we - // create a ref to it for the MaybeRenderEarlyManager. This is a hack; we - // should not be sending the CodecImage at all. The MaybeRenderEarlyManager - // should work with some other object that happens to be used by CodecImage, - // and non-GL things, to hold the output buffer, etc. - record.codec_image_holder = base::MakeRefCounted<CodecImageHolder>( - base::SequencedTaskRunnerHandle::Get(), std::move(codec_image)); - - std::move(image_ready_cb).Run(std::move(record)); -} - -bool GpuSharedImageVideoFactory::CreateImageInternal( - const SharedImageVideoProvider::ImageSpec& spec, - scoped_refptr<TextureOwner> texture_owner, - gpu::Mailbox mailbox, - scoped_refptr<CodecImage> image) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!MakeContextCurrent(stub_)) - return false; - - gpu::gles2::ContextGroup* group = stub_->decoder_context()->GetContextGroup(); - if (!group) - return false; - gpu::gles2::TextureManager* texture_manager = group->texture_manager(); - if (!texture_manager) - return false; - - const auto& size = spec.size; - - // Create a Texture and a CodecImage to back it. - // TODO(liberato): Once legacy mailbox support is removed, we don't need to - // create this texture. So, we won't need |texture_owner| either. - std::unique_ptr<AbstractTexture> texture = decoder_helper_->CreateTexture( - GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size.width(), size.height(), GL_RGBA, - GL_UNSIGNED_BYTE); - - // Attach the image to the texture. - // Either way, we expect this to be UNBOUND (i.e., decoder-managed). For - // overlays, BindTexImage will return true, causing it to transition to the - // BOUND state, and thus receive ScheduleOverlayPlane calls. For TextureOwner - // backed images, BindTexImage will return false, and CopyTexImage will be - // tried next. - // TODO(liberato): consider not binding this as a StreamTextureImage if we're - // using an overlay. There's no advantage. We'd likely want to create (and - // initialize to a 1x1 texture) a 2D texture above in that case, in case - // somebody tries to sample from it. Be sure that promotion hints still - // work properly, though -- they might require a stream texture image. - GLuint texture_owner_service_id = - texture_owner ? texture_owner->GetTextureId() : 0; - texture->BindStreamTextureImage(image.get(), texture_owner_service_id); - - gpu::ContextResult result; - auto shared_context = GetSharedContext(stub_, &result); - if (!shared_context) { - LOG(ERROR) << "GpuSharedImageVideoFactory: Unable to get a shared context."; - ContextStateResultUMA(result); - return false; - } - - // Create a shared image. - // TODO(vikassoni): Hardcoding colorspace to SRGB. Figure how if media has a - // colorspace and wire it here. - // TODO(vikassoni): This shared image need to be thread safe eventually for - // webview to work with shared images. - auto shared_image = std::make_unique<SharedImageVideo>( - mailbox, gfx::ColorSpace::CreateSRGB(), std::move(image), - std::move(texture), std::move(shared_context), - false /* is_thread_safe */); - - if (!ycbcr_info_) - ycbcr_info_ = shared_image->GetYcbcrInfo(); - - // Register it with shared image mailbox as well as legacy mailbox. This - // keeps |shared_image| around until its destruction cb is called. - // NOTE: Currently none of the video mailbox consumer uses shared image - // mailbox. - DCHECK(stub_->channel()->gpu_channel_manager()->shared_image_manager()); - stub_->channel()->shared_image_stub()->factory()->RegisterBacking( - std::move(shared_image), /* legacy_mailbox */ true); - - return true; -} - -void GpuSharedImageVideoFactory::OnWillDestroyStub(bool have_context) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(stub_); - stub_ = nullptr; - decoder_helper_ = nullptr; -} - } // namespace media
diff --git a/media/gpu/android/video_frame_factory_impl.h b/media/gpu/android/video_frame_factory_impl.h index 1fa62c4..c3befa30 100644 --- a/media/gpu/android/video_frame_factory_impl.h +++ b/media/gpu/android/video_frame_factory_impl.h
@@ -10,12 +10,7 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/single_thread_task_runner.h" -#include "gpu/command_buffer/service/abstract_texture.h" -#include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "gpu/command_buffer/service/shared_image_representation.h" -#include "gpu/command_buffer/service/texture_manager.h" -#include "gpu/ipc/common/vulkan_ycbcr_info.h" -#include "gpu/ipc/service/command_buffer_stub.h" +#include "gpu/config/gpu_preferences.h" #include "media/base/video_frame.h" #include "media/gpu/android/codec_image.h" #include "media/gpu/android/codec_wrapper.h" @@ -23,7 +18,6 @@ #include "media/gpu/android/shared_image_video_provider.h" #include "media/gpu/android/surface_texture_gl_owner.h" #include "media/gpu/android/video_frame_factory.h" -#include "media/gpu/gles2_decoder_helper.h" #include "media/gpu/media_gpu_export.h" #include "ui/gl/gl_bindings.h" @@ -48,8 +42,8 @@ // |get_stub_cb| will be run on |gpu_task_runner|. VideoFrameFactoryImpl( scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, - GetStubCb get_stub_cb, const gpu::GpuPreferences& gpu_preferences, + std::unique_ptr<SharedImageVideoProvider> image_provider, std::unique_ptr<MaybeRenderEarlyManager> mre_manager); ~VideoFrameFactoryImpl() override; @@ -93,7 +87,6 @@ std::unique_ptr<SharedImageVideoProvider> image_provider_; scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; - GetStubCb get_stub_cb_; // The texture owner that video frames should use, or nullptr. scoped_refptr<TextureOwner> texture_owner_; @@ -117,61 +110,6 @@ DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryImpl); }; -// GpuSharedImageVideoFactory creates SharedImageVideo objects. It must be run -// on the gpu main thread. -// -// GpuSharedImageVideoFactory is an implementation detail of -// DirectSharedImageVideoProvider. It really should be split out into its own -// file from here, but in the interest of making CL diffs more readable, that -// is left for later. -class GpuSharedImageVideoFactory - : public gpu::CommandBufferStub::DestructionObserver { - public: - GpuSharedImageVideoFactory(); - ~GpuSharedImageVideoFactory() override; - - // TODO(liberato): Now that this is used as part of an image provider, it - // doesn't really make sense for it to call back with a TextureOwner. That - // should be handled by VideoFrameFactoryImpl if it wants. - void Initialize(VideoFrameFactory::OverlayMode overlay_mode, - VideoFrameFactory::GetStubCb get_stub_cb, - VideoFrameFactory::InitCb init_cb); - - // Similar to SharedImageVideoProvider::ImageReadyCB, but provides additional - // details for the provider that's using us. - using FactoryImageReadyCB = SharedImageVideoProvider::ImageReadyCB; - - // Creates a SharedImage for |spec|, and returns it via the callback. - // TODO(liberato): |texture_owner| is only needed to get the service id, to - // create the per-frame texture. All of that is only needed for legacy - // mailbox support, where we have to have one texture per CodecImage. - void CreateImage(FactoryImageReadyCB cb, - const SharedImageVideoProvider::ImageSpec& spec, - scoped_refptr<TextureOwner> texture_owner); - - private: - // Creates a SharedImage for |mailbox|, and returns success or failure. - bool CreateImageInternal(const SharedImageVideoProvider::ImageSpec& spec, - scoped_refptr<TextureOwner> texture_owner, - gpu::Mailbox mailbox, - scoped_refptr<CodecImage> image); - - void OnWillDestroyStub(bool have_context) override; - - gpu::CommandBufferStub* stub_ = nullptr; - - // A helper for creating textures. Only valid while |stub_| is valid. - std::unique_ptr<GLES2DecoderHelper> decoder_helper_; - - // Sampler conversion information which is used in vulkan context. This is - // constant for all the frames in a video and hence we cache it. - base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_; - - THREAD_CHECKER(thread_checker_); - base::WeakPtrFactory<GpuSharedImageVideoFactory> weak_factory_; - DISALLOW_COPY_AND_ASSIGN(GpuSharedImageVideoFactory); -}; - } // namespace media #endif // MEDIA_GPU_ANDROID_VIDEO_FRAME_FACTORY_IMPL_
diff --git a/media/gpu/android/video_frame_factory_impl_unittest.cc b/media/gpu/android/video_frame_factory_impl_unittest.cc index c9ba386e..20b2812f 100644 --- a/media/gpu/android/video_frame_factory_impl_unittest.cc +++ b/media/gpu/android/video_frame_factory_impl_unittest.cc
@@ -6,45 +6,209 @@ #include "base/bind.h" #include "base/single_thread_task_runner.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/mock_callback.h" #include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" +#include "gpu/command_buffer/service/shared_context_state.h" #include "gpu/config/gpu_preferences.h" +#include "media/base/limits.h" #include "media/gpu/android/maybe_render_early_manager.h" +#include "media/gpu/android/shared_image_video_provider.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using base::test::RunOnceCallback; +using testing::_; +using testing::SaveArg; + namespace gpu { class CommandBufferStub; } // namespace gpu namespace media { -struct MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager { +class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager { + public: MOCK_METHOD1(SetSurfaceBundle, void(scoped_refptr<CodecSurfaceBundle>)); MOCK_METHOD1(AddCodecImage, void(scoped_refptr<CodecImageHolder>)); MOCK_METHOD0(MaybeRenderEarly, void()); }; +class MockSharedImageVideoProvider : public SharedImageVideoProvider { + public: + MockSharedImageVideoProvider() : spec_(gfx::Size(0, 0)) {} + + void Initialize(GpuInitCB gpu_init_cb) { Initialize_(gpu_init_cb); } + + MOCK_METHOD1(Initialize_, void(GpuInitCB& gpu_init_cb)); + + void RequestImage(ImageReadyCB cb, + const ImageSpec& spec, + std::unique_ptr<CodecOutputBuffer> output_buffer, + scoped_refptr<TextureOwner> texture_owner, + PromotionHintAggregator::NotifyPromotionHintCB + promotion_hint_cb) override { + cb_ = std::move(cb); + spec_ = spec; + output_buffer_ = std::move(output_buffer); + texture_owner_ = std::move(texture_owner); + promotion_hint_cb_ = std::move(promotion_hint_cb); + + MockRequestImage(); + } + + MOCK_METHOD0(MockRequestImage, void()); + + // Most recent arguments to RequestImage. + ImageReadyCB cb_; + ImageSpec spec_; + std::unique_ptr<CodecOutputBuffer> output_buffer_; + scoped_refptr<TextureOwner> texture_owner_; + PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb_; +}; + class VideoFrameFactoryImplTest : public testing::Test { public: VideoFrameFactoryImplTest() - : task_runner_(base::ThreadTaskRunnerHandle::Get()) {} + : task_runner_(base::ThreadTaskRunnerHandle::Get()) { + auto get_stub_cb = base::BindRepeating( + []() -> gpu::CommandBufferStub* { return nullptr; }); + + auto image_provider = std::make_unique<MockSharedImageVideoProvider>(); + image_provider_raw_ = image_provider.get(); + + auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>(); + mre_manager_raw_ = mre_manager.get(); + + impl_ = std::make_unique<VideoFrameFactoryImpl>( + task_runner_, gpu_preferences_, std::move(image_provider), + std::move(mre_manager)); + } ~VideoFrameFactoryImplTest() override = default; + void RequestVideoFrame() { + gfx::Size coded_size(100, 100); + gfx::Rect visible_rect(coded_size); + gfx::Size natural_size(coded_size); + auto output_buffer = CodecOutputBuffer::CreateForTesting(0, coded_size); + ASSERT_TRUE( + VideoFrame::IsValidConfig(PIXEL_FORMAT_ARGB, VideoFrame::STORAGE_OPAQUE, + coded_size, visible_rect, natural_size)); + + // We should get a call to the output callback, but no calls to the + // provider. + // TODO(liberato): Verify that it's sending the proper TextureOwner. + // However, we haven't actually given it a TextureOwner yet. + CodecOutputBuffer* output_buffer_raw = output_buffer.get(); + EXPECT_CALL(*image_provider_raw_, MockRequestImage()); + + impl_->CreateVideoFrame( + std::move(output_buffer), base::TimeDelta(), natural_size, + PromotionHintAggregator::NotifyPromotionHintCB(), output_cb_.Get()); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(image_provider_raw_->output_buffer_.get(), output_buffer_raw); + } + base::test::ScopedTaskEnvironment scoped_task_environment_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + std::unique_ptr<VideoFrameFactoryImpl> impl_; + + MockMaybeRenderEarlyManager* mre_manager_raw_ = nullptr; + MockSharedImageVideoProvider* image_provider_raw_ = nullptr; + + // Sent to |impl_| by RequestVideoFrame.. + base::MockCallback<VideoFrameFactory::OnceOutputCb> output_cb_; + gpu::GpuPreferences gpu_preferences_; }; -TEST_F(VideoFrameFactoryImplTest, CreateDoesntCrash) { - // Placeholder test until we pull out the SharedImageVideoProvider. - auto get_stub_cb = - base::BindRepeating([]() -> gpu::CommandBufferStub* { return nullptr; }); - std::unique_ptr<VideoFrameFactoryImpl> impl = - std::make_unique<VideoFrameFactoryImpl>( - task_runner_, get_stub_cb, gpu_preferences_, - std::make_unique<MockMaybeRenderEarlyManager>()); +TEST_F(VideoFrameFactoryImplTest, ImageProviderInitFailure) { + // If the image provider fails to init, then our init cb should be called with + // no TextureOwner. + EXPECT_CALL(*image_provider_raw_, Initialize_(_)) + .Times(1) + .WillOnce(RunOnceCallback<0>(nullptr)); + base::MockCallback<VideoFrameFactory::InitCb> init_cb; + EXPECT_CALL(init_cb, Run(scoped_refptr<TextureOwner>(nullptr))); + impl_->Initialize(VideoFrameFactory::OverlayMode::kDontRequestPromotionHints, + init_cb.Get()); + base::RunLoop().RunUntilIdle(); + + // TODO(liberato): for testing, we could just skip calling the gpu init cb, + // since |impl_| doesn't know or care if it's called. that way, we don't need + // to mock out making the callback work. would be nice, though. +} + +TEST_F(VideoFrameFactoryImplTest, + SetSurfaceBundleForwardsToMaybeRenderEarlyManager) { + // Sending a non-null CodecSurfaceBundle should forward it to |mre_manager|. + scoped_refptr<CodecSurfaceBundle> surface_bundle = + base::MakeRefCounted<CodecSurfaceBundle>(); + EXPECT_CALL(*mre_manager_raw_, SetSurfaceBundle(surface_bundle)); + impl_->SetSurfaceBundle(surface_bundle); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(VideoFrameFactoryImplTest, CreateVideoFrameFailsIfUnsupportedFormat) { + // Sending an unsupported format should cause an early failure, without a + // thread hop. + gfx::Size coded_size(limits::kMaxDimension + 1, limits::kMaxDimension + 1); + gfx::Rect visible_rect(coded_size); + gfx::Size natural_size(0, 0); + auto output_buffer = CodecOutputBuffer::CreateForTesting(0, coded_size); + ASSERT_FALSE(VideoFrame::IsValidConfig(PIXEL_FORMAT_ARGB, + VideoFrame::STORAGE_OPAQUE, coded_size, + visible_rect, natural_size)); + + // We should get a call to the output callback, but no calls to the provider. + base::MockCallback<VideoFrameFactory::OnceOutputCb> output_cb; + EXPECT_CALL(output_cb, Run(scoped_refptr<VideoFrame>(nullptr))); + EXPECT_CALL(*image_provider_raw_, MockRequestImage()).Times(0); + + impl_->CreateVideoFrame( + std::move(output_buffer), base::TimeDelta(), natural_size, + PromotionHintAggregator::NotifyPromotionHintCB(), output_cb.Get()); + base::RunLoop().RunUntilIdle(); +} + +TEST_F(VideoFrameFactoryImplTest, CreateVideoFrameSucceeds) { + // Creating a video frame calls through to the image provider, and forwards a + // VideoFrame to the output cb. + // + // TODO(liberato): Consider testing the metadata values. + RequestVideoFrame(); + + // Call the ImageReadyCB. + scoped_refptr<VideoFrame> frame; + EXPECT_CALL(output_cb_, Run(_)).WillOnce(SaveArg<0>(&frame)); + SharedImageVideoProvider::ImageRecord record; + record.mailbox = gpu::Mailbox::Generate(); + bool release_cb_called_flag = false; + record.release_cb = + base::BindOnce([](bool* flag, const gpu::SyncToken&) { *flag = true; }, + base::Unretained(&release_cb_called_flag)); + record.codec_image_holder = nullptr; + std::move(image_provider_raw_->cb_).Run(std::move(record)); + base::RunLoop().RunUntilIdle(); + EXPECT_NE(frame, nullptr); + + // Destroy the VideoFrame, and verify that our release cb is called. + EXPECT_FALSE(release_cb_called_flag); + frame = nullptr; + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(release_cb_called_flag); +} + +TEST_F(VideoFrameFactoryImplTest, + DestroyingFactoryDuringVideoFrameCreationDoesntCrash) { + // We should be able to destroy |impl_| while a VideoFrame is pending, and + // nothing bad should happen. + RequestVideoFrame(); + impl_ = nullptr; + base::RunLoop().RunUntilIdle(); } } // namespace media
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc index 4a24fb4..634792b 100644 --- a/media/mojo/services/gpu_mojo_media_client.cc +++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -30,6 +30,7 @@ #include "media/filters/android/media_codec_audio_decoder.h" #include "media/gpu/android/android_video_surface_chooser_impl.h" #include "media/gpu/android/codec_allocator.h" +#include "media/gpu/android/direct_shared_image_video_provider.h" #include "media/gpu/android/maybe_render_early_manager.h" #include "media/gpu/android/media_codec_video_decoder.h" #include "media/gpu/android/video_frame_factory_impl.h" @@ -203,6 +204,8 @@ auto get_stub_cb = base::Bind(&GetCommandBufferStub, media_gpu_channel_manager_, command_buffer_id->channel_token, command_buffer_id->route_id); + auto image_provider = std::make_unique<DirectSharedImageVideoProvider>( + gpu_task_runner_, std::move(get_stub_cb)); video_decoder = std::make_unique<MediaCodecVideoDecoder>( gpu_preferences_, gpu_feature_info_, DeviceInfo::GetInstance(), CodecAllocator::GetInstance(gpu_task_runner_), @@ -210,7 +213,7 @@ DeviceInfo::GetInstance()->IsSetOutputSurfaceSupported()), android_overlay_factory_cb_, std::move(request_overlay_info_cb), std::make_unique<VideoFrameFactoryImpl>( - gpu_task_runner_, std::move(get_stub_cb), gpu_preferences_, + gpu_task_runner_, gpu_preferences_, std::move(image_provider), MaybeRenderEarlyManager::Create(gpu_task_runner_))); #elif defined(OS_CHROMEOS)
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc index 88c5bf9..f158f31 100644 --- a/printing/backend/cups_printer.cc +++ b/printing/backend/cups_printer.cc
@@ -178,7 +178,7 @@ http_status_t start_doc_status = cupsStartDestDocument(cups_http_, destination_.get(), dest_info_.get(), job_id, document_name.c_str(), CUPS_FORMAT_PDF, - options.size(), data, last_document ? 0 : 1); + options.size(), data, last_document ? 1 : 0); cupsSetUser(nullptr); // reset to default username ("anonymous") return start_doc_status == HTTP_CONTINUE;
diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h index 29ba0b74..52c0db1 100644 --- a/sandbox/win/src/sandbox_policy.h +++ b/sandbox/win/src/sandbox_policy.h
@@ -20,7 +20,7 @@ class TargetPolicy { public: // Windows subsystems that can have specific rules. - // Note: The process subsystem(SUBSY_PROCESS) does not evaluate the request + // Note: The process subsystem(SUBSYS_PROCESS) does not evaluate the request // exactly like the CreateProcess API does. See the comment at the top of // process_thread_dispatcher.cc for more details. enum SubSystem {
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn index 4f61fe3d..64d14fa 100644 --- a/services/device/BUILD.gn +++ b/services/device/BUILD.gn
@@ -15,6 +15,7 @@ # dependence should only be for the purpose of embedding the Device Service. visibility = [ ":test_support", + ":tests", "//content/browser", ] sources = [ @@ -124,6 +125,7 @@ ] deps = [ + ":lib", ":test_support", "//base", "//base/test:test_support",
diff --git a/services/device/device_service.cc b/services/device/device_service.cc index 127ae6b8..180e5514 100644 --- a/services/device/device_service.cc +++ b/services/device/device_service.cc
@@ -15,6 +15,7 @@ #include "mojo/public/cpp/system/message_pipe.h" #include "services/device/bluetooth/bluetooth_system_factory.h" #include "services/device/fingerprint/fingerprint.h" +#include "services/device/generic_sensor/platform_sensor_provider.h" #include "services/device/generic_sensor/sensor_provider_impl.h" #include "services/device/geolocation/geolocation_config.h" #include "services/device/geolocation/geolocation_context.h" @@ -134,6 +135,12 @@ #endif } +void DeviceService::SetPlatformSensorProviderForTesting( + std::unique_ptr<PlatformSensorProvider> provider) { + DCHECK(!sensor_provider_); + sensor_provider_ = std::make_unique<SensorProviderImpl>(std::move(provider)); +} + void DeviceService::OnStart() { registry_.AddInterface<mojom::Fingerprint>(base::Bind( &DeviceService::BindFingerprintRequest, base::Unretained(this))); @@ -314,11 +321,14 @@ void DeviceService::BindSensorProviderRequest( mojom::SensorProviderRequest request) { - if (io_task_runner_) { - io_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&device::SensorProviderImpl::Create, - std::move(request))); + if (!sensor_provider_) { + auto platform_provider = PlatformSensorProvider::Create(); + if (!platform_provider) + return; + sensor_provider_ = + std::make_unique<SensorProviderImpl>(std::move(platform_provider)); } + sensor_provider_->Bind(std::move(request)); } void DeviceService::BindTimeZoneMonitorRequest(
diff --git a/services/device/device_service.h b/services/device/device_service.h index a6261c13..ce6cf971 100644 --- a/services/device/device_service.h +++ b/services/device/device_service.h
@@ -72,8 +72,10 @@ #endif class DeviceService; +class PlatformSensorProvider; class PowerMonitorMessageBroadcaster; class PublicIpAddressLocationNotifier; +class SensorProviderImpl; class TimeZoneMonitor; #if defined(OS_ANDROID) @@ -125,6 +127,9 @@ #endif ~DeviceService() override; + void SetPlatformSensorProviderForTesting( + std::unique_ptr<PlatformSensorProvider> provider); + private: // service_manager::Service: void OnStart() override; @@ -178,6 +183,7 @@ power_monitor_message_broadcaster_; std::unique_ptr<PublicIpAddressGeolocationProvider> public_ip_address_geolocation_provider_; + std::unique_ptr<SensorProviderImpl> sensor_provider_; std::unique_ptr<TimeZoneMonitor> time_zone_monitor_; std::unique_ptr<usb::DeviceManagerImpl> usb_device_manager_; std::unique_ptr<usb::DeviceManagerTest> usb_device_manager_test_;
diff --git a/services/device/device_service_test_base.h b/services/device/device_service_test_base.h index c2b83e8..2343cf2 100644 --- a/services/device/device_service_test_base.h +++ b/services/device/device_service_test_base.h
@@ -31,6 +31,7 @@ protected: service_manager::Connector* connector() { return connector_.get(); } + DeviceService* device_service() { return service_.get(); } // Can optionally be called to destroy the service before a child test fixture // shuts down, in case the DeviceService has dependencies on objects created
diff --git a/services/device/generic_sensor/generic_sensor_service_unittest.cc b/services/device/generic_sensor/generic_sensor_service_unittest.cc index a442982..e46fea7 100644 --- a/services/device/generic_sensor/generic_sensor_service_unittest.cc +++ b/services/device/generic_sensor/generic_sensor_service_unittest.cc
@@ -6,21 +6,14 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/singleton.h" #include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/waitable_event.h" #include "base/test/scoped_feature_list.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "device/base/synchronization/one_writer_seqlock.h" #include "mojo/public/cpp/bindings/binding.h" +#include "services/device/device_service.h" #include "services/device/device_service_test_base.h" #include "services/device/generic_sensor/fake_platform_sensor_and_provider.h" #include "services/device/generic_sensor/platform_sensor.h" #include "services/device/generic_sensor/platform_sensor_provider.h" -#include "services/device/generic_sensor/sensor_provider_impl.h" #include "services/device/public/cpp/device_features.h" #include "services/device/public/cpp/generic_sensor/sensor_reading.h" #include "services/device/public/cpp/generic_sensor/sensor_traits.h" @@ -154,51 +147,23 @@ class GenericSensorServiceTest : public DeviceServiceTestBase { public: - GenericSensorServiceTest() - : io_loop_finished_event_( - base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED) {} + GenericSensorServiceTest() = default; void SetUp() override { scoped_feature_list_.InitWithFeatures( {features::kGenericSensor, features::kGenericSensorExtraClasses}, {}); DeviceServiceTestBase::SetUp(); - io_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GenericSensorServiceTest::SetUpOnIOThread, - base::Unretained(this))); - io_loop_finished_event_.Wait(); + fake_platform_sensor_provider_ = new FakePlatformSensorProvider(); + device_service()->SetPlatformSensorProviderForTesting( + base::WrapUnique(fake_platform_sensor_provider_)); connector()->BindInterface(mojom::kServiceName, &sensor_provider_); } - void TearDown() override { - io_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&GenericSensorServiceTest::TearDownOnIOThread, - base::Unretained(this))); - io_loop_finished_event_.Wait(); - } - - void SetUpOnIOThread() { - fake_platform_sensor_provider_ = new FakePlatformSensorProvider(); - PlatformSensorProvider::SetProviderForTesting( - fake_platform_sensor_provider_); - io_loop_finished_event_.Signal(); - } - - void TearDownOnIOThread() { - PlatformSensorProvider::SetProviderForTesting(nullptr); - - DCHECK(fake_platform_sensor_provider_); - delete fake_platform_sensor_provider_; - fake_platform_sensor_provider_ = nullptr; - - io_loop_finished_event_.Signal(); - } mojom::SensorProviderPtr sensor_provider_; - base::WaitableEvent io_loop_finished_event_; base::test::ScopedFeatureList scoped_feature_list_; - // FakePlatformSensorProvider must be created and deleted in IO thread. + // This object is owned by the DeviceService instance. FakePlatformSensorProvider* fake_platform_sensor_provider_; DISALLOW_COPY_AND_ASSIGN(GenericSensorServiceTest);
diff --git a/services/device/generic_sensor/platform_sensor_and_provider_unittest.cc b/services/device/generic_sensor/platform_sensor_and_provider_unittest.cc index 82b83f9..b8dbcf0 100644 --- a/services/device/generic_sensor/platform_sensor_and_provider_unittest.cc +++ b/services/device/generic_sensor/platform_sensor_and_provider_unittest.cc
@@ -21,7 +21,6 @@ public: PlatformSensorProviderTest() { provider_ = std::make_unique<FakePlatformSensorProvider>(); - PlatformSensorProvider::SetProviderForTesting(provider_.get()); } protected:
diff --git a/services/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc b/services/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc index 84117b4..cc62c56 100644 --- a/services/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc +++ b/services/device/generic_sensor/platform_sensor_and_provider_unittest_win.cc
@@ -3,7 +3,6 @@ // found in the LICENSE file. #include <SensorsApi.h> -#include <objbase.h> #include <sensors.h> #include "base/bind.h" @@ -12,6 +11,7 @@ #include "base/test/scoped_task_environment.h" #include "base/win/iunknown_impl.h" #include "base/win/propvarutil.h" +#include "base/win/scoped_com_initializer.h" #include "base/win/scoped_propvariant.h" #include "services/device/generic_sensor/fake_platform_sensor_and_provider.h" #include "services/device/generic_sensor/generic_sensor_consts.h" @@ -195,7 +195,6 @@ base::test::ScopedTaskEnvironment::MainThreadType::IO) {} void SetUp() override { - EXPECT_EQ(S_OK, CoInitialize(nullptr)); sensor_ = new NiceMock<MockISensor>(); sensor_collection_ = new NiceMock<MockISensorCollection>(); sensor_manager_ = new NiceMock<MockISensorManager>(); @@ -203,14 +202,8 @@ sensor_manager_->QueryInterface(IID_PPV_ARGS(&manager)); // Overrides default ISensorManager with mocked interface. - PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesting( - manager); - } - - void TearDown() override { - Microsoft::WRL::ComPtr<ISensorManager> null_manager; - PlatformSensorProviderWin::GetInstance()->SetSensorManagerForTesting( - null_manager); + provider_ = std::make_unique<PlatformSensorProviderWin>(); + provider_->SetSensorManagerForTesting(std::move(manager)); } protected: @@ -223,7 +216,7 @@ // PlatformSensorProvider::CreateSensorCallback completion. scoped_refptr<PlatformSensor> CreateSensor(mojom::SensorType type) { run_loop_ = std::make_unique<base::RunLoop>(); - PlatformSensorProviderWin::GetInstance()->CreateSensor( + provider_->CreateSensor( type, base::Bind(&PlatformSensorAndProviderTestWin::SensorCreated, base::Unretained(this))); run_loop_->Run(); @@ -388,10 +381,12 @@ sensor_events_->OnDataUpdated(sensor_.get(), data_report.Get()); } + base::win::ScopedCOMInitializer com_initializer_; base::test::ScopedTaskEnvironment scoped_task_environment_; scoped_refptr<MockISensorManager> sensor_manager_; scoped_refptr<MockISensorCollection> sensor_collection_; scoped_refptr<MockISensor> sensor_; + std::unique_ptr<PlatformSensorProviderWin> provider_; Microsoft::WRL::ComPtr<ISensorEvents> sensor_events_; scoped_refptr<PlatformSensor> platform_sensor_; // Inner run loop used to wait for async sensor creation callback. @@ -541,8 +536,7 @@ // Tests that Accelerometer readings are correctly converted. TEST_F(PlatformSensorAndProviderTestWin, CheckAccelerometerReadingConversion) { - mojo::ScopedSharedBufferHandle handle = - PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); + mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle(); mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( sizeof(SensorReadingSharedBuffer), SensorReadingSharedBuffer::GetOffset(SensorType::ACCELEROMETER)); @@ -580,8 +574,7 @@ // Tests that Gyroscope readings are correctly converted. TEST_F(PlatformSensorAndProviderTestWin, CheckGyroscopeReadingConversion) { - mojo::ScopedSharedBufferHandle handle = - PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); + mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle(); mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( sizeof(SensorReadingSharedBuffer), SensorReadingSharedBuffer::GetOffset(SensorType::GYROSCOPE)); @@ -620,8 +613,7 @@ // Tests that Magnetometer readings are correctly converted. TEST_F(PlatformSensorAndProviderTestWin, CheckMagnetometerReadingConversion) { - mojo::ScopedSharedBufferHandle handle = - PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); + mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle(); mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset( sizeof(SensorReadingSharedBuffer), SensorReadingSharedBuffer::GetOffset(SensorType::MAGNETOMETER)); @@ -662,8 +654,7 @@ // provided. TEST_F(PlatformSensorAndProviderTestWin, CheckDeviceOrientationEulerAnglesReadingConversion) { - mojo::ScopedSharedBufferHandle handle = - PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); + mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle(); mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset(sizeof(SensorReadingSharedBuffer), SensorReadingSharedBuffer::GetOffset( @@ -705,8 +696,7 @@ // provided. TEST_F(PlatformSensorAndProviderTestWin, CheckDeviceOrientationQuaternionReadingConversion) { - mojo::ScopedSharedBufferHandle handle = - PlatformSensorProviderWin::GetInstance()->CloneSharedBufferHandle(); + mojo::ScopedSharedBufferHandle handle = provider_->CloneSharedBufferHandle(); mojo::ScopedSharedBufferMapping mapping = handle->MapAtOffset(sizeof(SensorReadingSharedBuffer), SensorReadingSharedBuffer::GetOffset(
diff --git a/services/device/generic_sensor/platform_sensor_fusion_unittest.cc b/services/device/generic_sensor/platform_sensor_fusion_unittest.cc index 8fa9ae4..3a48154 100644 --- a/services/device/generic_sensor/platform_sensor_fusion_unittest.cc +++ b/services/device/generic_sensor/platform_sensor_fusion_unittest.cc
@@ -30,7 +30,6 @@ public: PlatformSensorFusionTest() { provider_ = std::make_unique<FakePlatformSensorProvider>(); - PlatformSensorProvider::SetProviderForTesting(provider_.get()); } protected:
diff --git a/services/device/generic_sensor/platform_sensor_provider.cc b/services/device/generic_sensor/platform_sensor_provider.cc index c7cc03f8..aeb0f32 100644 --- a/services/device/generic_sensor/platform_sensor_provider.cc +++ b/services/device/generic_sensor/platform_sensor_provider.cc
@@ -14,32 +14,18 @@ #include "services/device/generic_sensor/platform_sensor_provider_linux.h" #endif -namespace { - -static device::PlatformSensorProvider* g_provider_for_testing = nullptr; - -} // namespace - namespace device { // static -void PlatformSensorProvider::SetProviderForTesting( - PlatformSensorProvider* provider) { - g_provider_for_testing = provider; -} - -// static -PlatformSensorProvider* PlatformSensorProvider::GetInstance() { - if (g_provider_for_testing) - return g_provider_for_testing; +std::unique_ptr<PlatformSensorProvider> PlatformSensorProvider::Create() { #if defined(OS_MACOSX) - return PlatformSensorProviderMac::GetInstance(); + return std::make_unique<PlatformSensorProviderMac>(); #elif defined(OS_ANDROID) - return PlatformSensorProviderAndroid::GetInstance(); + return std::make_unique<PlatformSensorProviderAndroid>(); #elif defined(OS_WIN) - return PlatformSensorProviderWin::GetInstance(); + return std::make_unique<PlatformSensorProviderWin>(); #elif defined(OS_LINUX) && defined(USE_UDEV) - return PlatformSensorProviderLinux::GetInstance(); + return std::make_unique<PlatformSensorProviderLinux>(); #else return nullptr; #endif
diff --git a/services/device/generic_sensor/platform_sensor_provider.h b/services/device/generic_sensor/platform_sensor_provider.h index e9777f9..525d81f 100644 --- a/services/device/generic_sensor/platform_sensor_provider.h +++ b/services/device/generic_sensor/platform_sensor_provider.h
@@ -5,27 +5,25 @@ #ifndef SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_PROVIDER_H_ #define SERVICES_DEVICE_GENERIC_SENSOR_PLATFORM_SENSOR_PROVIDER_H_ +#include <memory> + #include "services/device/generic_sensor/platform_sensor_provider_base.h" namespace device { -// This a singleton class returning the actual sensor provider -// implementation for the current platform. +// This the base class for platform-specific sensor provider implementations. +// In typical usage a single instance is owned by DeviceService. class PlatformSensorProvider : public PlatformSensorProviderBase { public: - // Returns the PlatformSensorProvider singleton. + // Returns a PlatformSensorProvider for the current platform. // Note: returns 'nullptr' if there is no available implementation for // the current platform. - static PlatformSensorProvider* GetInstance(); + static std::unique_ptr<PlatformSensorProvider> Create(); - // This method allows to set a provider for testing and therefore - // skip the default platform provider. This allows testing without - // relying on the platform provider. - static void SetProviderForTesting(PlatformSensorProvider* provider); + ~PlatformSensorProvider() override = default; protected: PlatformSensorProvider() = default; - ~PlatformSensorProvider() override = default; DISALLOW_COPY_AND_ASSIGN(PlatformSensorProvider); };
diff --git a/services/device/generic_sensor/platform_sensor_provider_android.cc b/services/device/generic_sensor/platform_sensor_provider_android.cc index b7ce5f7..6348b759 100644 --- a/services/device/generic_sensor/platform_sensor_provider_android.cc +++ b/services/device/generic_sensor/platform_sensor_provider_android.cc
@@ -24,13 +24,6 @@ namespace device { -// static -PlatformSensorProviderAndroid* PlatformSensorProviderAndroid::GetInstance() { - return base::Singleton< - PlatformSensorProviderAndroid, - base::LeakySingletonTraits<PlatformSensorProviderAndroid>>::get(); -} - PlatformSensorProviderAndroid::PlatformSensorProviderAndroid() { JNIEnv* env = AttachCurrentThread(); j_object_.Reset(Java_PlatformSensorProvider_create(env));
diff --git a/services/device/generic_sensor/platform_sensor_provider_android.h b/services/device/generic_sensor/platform_sensor_provider_android.h index 0ff21aed..70aa119 100644 --- a/services/device/generic_sensor/platform_sensor_provider_android.h +++ b/services/device/generic_sensor/platform_sensor_provider_android.h
@@ -16,8 +16,6 @@ PlatformSensorProviderAndroid(); ~PlatformSensorProviderAndroid() override; - static PlatformSensorProviderAndroid* GetInstance(); - void SetSensorManagerToNullForTesting(); protected:
diff --git a/services/device/generic_sensor/platform_sensor_provider_linux.cc b/services/device/generic_sensor/platform_sensor_provider_linux.cc index a6f4bbf8..8a1b19f 100644 --- a/services/device/generic_sensor/platform_sensor_provider_linux.cc +++ b/services/device/generic_sensor/platform_sensor_provider_linux.cc
@@ -43,13 +43,6 @@ } } // namespace -// static -PlatformSensorProviderLinux* PlatformSensorProviderLinux::GetInstance() { - return base::Singleton< - PlatformSensorProviderLinux, - base::LeakySingletonTraits<PlatformSensorProviderLinux>>::get(); -} - PlatformSensorProviderLinux::PlatformSensorProviderLinux() : sensor_nodes_enumerated_(false), sensor_nodes_enumeration_started_(false),
diff --git a/services/device/generic_sensor/platform_sensor_provider_linux.h b/services/device/generic_sensor/platform_sensor_provider_linux.h index 1373787..9f1b686 100644 --- a/services/device/generic_sensor/platform_sensor_provider_linux.h +++ b/services/device/generic_sensor/platform_sensor_provider_linux.h
@@ -11,11 +11,6 @@ #include "base/sequenced_task_runner.h" #include "services/device/generic_sensor/linux/sensor_device_manager.h" -namespace base { -template <typename T> -struct DefaultSingletonTraits; -} // namespace base - namespace device { struct SensorInfoLinux; @@ -23,15 +18,14 @@ class PlatformSensorProviderLinux : public PlatformSensorProvider, public SensorDeviceManager::Delegate { public: - static PlatformSensorProviderLinux* GetInstance(); + PlatformSensorProviderLinux(); + ~PlatformSensorProviderLinux() override; // Sets another service provided by tests. void SetSensorDeviceManagerForTesting( std::unique_ptr<SensorDeviceManager> sensor_device_manager); protected: - ~PlatformSensorProviderLinux() override; - void CreateSensorInternal(mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer, const CreateSensorCallback& callback) override; @@ -39,8 +33,6 @@ void FreeResources() override; private: - friend struct base::DefaultSingletonTraits<PlatformSensorProviderLinux>; - friend class PlatformSensorAndProviderLinuxTest; // This is also needed for testing, as we create one provider per test, and @@ -50,8 +42,6 @@ using SensorDeviceMap = std::unordered_map<mojom::SensorType, std::unique_ptr<SensorInfoLinux>>; - PlatformSensorProviderLinux(); - void SensorDeviceFound( mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer,
diff --git a/services/device/generic_sensor/platform_sensor_provider_mac.cc b/services/device/generic_sensor/platform_sensor_provider_mac.cc index 46b4282..9954e88 100644 --- a/services/device/generic_sensor/platform_sensor_provider_mac.cc +++ b/services/device/generic_sensor/platform_sensor_provider_mac.cc
@@ -4,7 +4,6 @@ #include "services/device/generic_sensor/platform_sensor_provider_mac.h" -#include "base/memory/singleton.h" #include "services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.h" #include "services/device/generic_sensor/platform_sensor_accelerometer_mac.h" #include "services/device/generic_sensor/platform_sensor_ambient_light_mac.h" @@ -13,13 +12,6 @@ namespace device { -// static -PlatformSensorProviderMac* PlatformSensorProviderMac::GetInstance() { - return base::Singleton< - PlatformSensorProviderMac, - base::LeakySingletonTraits<PlatformSensorProviderMac>>::get(); -} - PlatformSensorProviderMac::PlatformSensorProviderMac() = default; PlatformSensorProviderMac::~PlatformSensorProviderMac() = default;
diff --git a/services/device/generic_sensor/platform_sensor_provider_mac.h b/services/device/generic_sensor/platform_sensor_provider_mac.h index 79d11b1f..89520ac 100644 --- a/services/device/generic_sensor/platform_sensor_provider_mac.h +++ b/services/device/generic_sensor/platform_sensor_provider_mac.h
@@ -14,8 +14,6 @@ PlatformSensorProviderMac(); ~PlatformSensorProviderMac() override; - static PlatformSensorProviderMac* GetInstance(); - protected: void CreateSensorInternal(mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer,
diff --git a/services/device/generic_sensor/platform_sensor_provider_unittest_android.cc b/services/device/generic_sensor/platform_sensor_provider_unittest_android.cc index 3391bef..7e81e15 100644 --- a/services/device/generic_sensor/platform_sensor_provider_unittest_android.cc +++ b/services/device/generic_sensor/platform_sensor_provider_unittest_android.cc
@@ -4,6 +4,8 @@ #include "services/device/generic_sensor/platform_sensor_provider_android.h" +#include <memory> + #include "base/bind.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -15,8 +17,7 @@ PlatformSensorProviderTestAndroid() = default; void SetUp() override { - provider_ = PlatformSensorProviderAndroid::GetInstance(); - ASSERT_TRUE(provider_); + provider_ = std::make_unique<PlatformSensorProviderAndroid>(); } void CreateSensorCallback(scoped_refptr<PlatformSensor> sensor) { @@ -24,7 +25,7 @@ } protected: - PlatformSensorProviderAndroid* provider_; + std::unique_ptr<PlatformSensorProviderAndroid> provider_; private: DISALLOW_COPY_AND_ASSIGN(PlatformSensorProviderTestAndroid);
diff --git a/services/device/generic_sensor/platform_sensor_provider_win.cc b/services/device/generic_sensor/platform_sensor_provider_win.cc index 2ddc7d28..56ade2ba 100644 --- a/services/device/generic_sensor/platform_sensor_provider_win.cc +++ b/services/device/generic_sensor/platform_sensor_provider_win.cc
@@ -10,7 +10,6 @@ #include <iomanip> #include "base/bind.h" -#include "base/memory/singleton.h" #include "base/task/post_task.h" #include "base/task/task_traits.h" #include "base/task_runner_util.h" @@ -22,24 +21,17 @@ namespace device { -// static -PlatformSensorProviderWin* PlatformSensorProviderWin::GetInstance() { - return base::Singleton< - PlatformSensorProviderWin, - base::LeakySingletonTraits<PlatformSensorProviderWin>>::get(); -} - -void PlatformSensorProviderWin::SetSensorManagerForTesting( - Microsoft::WRL::ComPtr<ISensorManager> sensor_manager) { - sensor_manager_ = sensor_manager; -} - PlatformSensorProviderWin::PlatformSensorProviderWin() : com_sta_task_runner_(base::CreateCOMSTATaskRunnerWithTraits( base::TaskPriority::USER_VISIBLE)) {} PlatformSensorProviderWin::~PlatformSensorProviderWin() = default; +void PlatformSensorProviderWin::SetSensorManagerForTesting( + Microsoft::WRL::ComPtr<ISensorManager> sensor_manager) { + sensor_manager_ = sensor_manager; +} + void PlatformSensorProviderWin::CreateSensorInternal( mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer,
diff --git a/services/device/generic_sensor/platform_sensor_provider_win.h b/services/device/generic_sensor/platform_sensor_provider_win.h index 9958ceba..9054d892 100644 --- a/services/device/generic_sensor/platform_sensor_provider_win.h +++ b/services/device/generic_sensor/platform_sensor_provider_win.h
@@ -10,11 +10,6 @@ #include "services/device/generic_sensor/platform_sensor_provider.h" -namespace base { -template <typename T> -struct DefaultSingletonTraits; -} // namespace base - namespace device { class PlatformSensorReaderWin; @@ -26,7 +21,8 @@ // - Constructs PlatformSensorWin on IPC thread and returns it to requester. class PlatformSensorProviderWin final : public PlatformSensorProvider { public: - static PlatformSensorProviderWin* GetInstance(); + PlatformSensorProviderWin(); + ~PlatformSensorProviderWin() override; // Overrides ISensorManager COM interface provided by the system, used // only for testing purposes. @@ -34,18 +30,12 @@ Microsoft::WRL::ComPtr<ISensorManager> sensor_manager); protected: - ~PlatformSensorProviderWin() override; - // PlatformSensorProvider interface implementation. void CreateSensorInternal(mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer, const CreateSensorCallback& callback) override; private: - friend struct base::DefaultSingletonTraits<PlatformSensorProviderWin>; - - PlatformSensorProviderWin(); - void InitSensorManager(); void OnInitSensorManager(mojom::SensorType type, SensorReadingSharedBuffer* reading_buffer,
diff --git a/services/device/generic_sensor/sensor_provider_impl.cc b/services/device/generic_sensor/sensor_provider_impl.cc index 7851de5..0c9fe68 100644 --- a/services/device/generic_sensor/sensor_provider_impl.cc +++ b/services/device/generic_sensor/sensor_provider_impl.cc
@@ -37,23 +37,18 @@ } // namespace -// static -void SensorProviderImpl::Create( - mojom::SensorProviderRequest request) { - PlatformSensorProvider* provider = PlatformSensorProvider::GetInstance(); - if (provider) { - mojo::MakeStrongBinding(base::WrapUnique(new SensorProviderImpl(provider)), - std::move(request)); - } -} - -SensorProviderImpl::SensorProviderImpl(PlatformSensorProvider* provider) - : provider_(provider) { +SensorProviderImpl::SensorProviderImpl( + std::unique_ptr<PlatformSensorProvider> provider) + : provider_(std::move(provider)) { DCHECK(provider_); } SensorProviderImpl::~SensorProviderImpl() {} +void SensorProviderImpl::Bind(mojom::SensorProviderRequest request) { + bindings_.AddBinding(this, std::move(request)); +} + void SensorProviderImpl::GetSensor(mojom::SensorType type, GetSensorCallback callback) { if (!base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses) && @@ -99,8 +94,8 @@ init_params->client_request = sensor_impl->GetClient(); mojom::SensorPtrInfo sensor_ptr_info; - mojo::MakeStrongBinding(std::move(sensor_impl), - mojo::MakeRequest(&sensor_ptr_info)); + sensor_bindings_.AddBinding(std::move(sensor_impl), + mojo::MakeRequest(&sensor_ptr_info)); init_params->sensor = std::move(sensor_ptr_info); init_params->memory = std::move(cloned_handle);
diff --git a/services/device/generic_sensor/sensor_provider_impl.h b/services/device/generic_sensor/sensor_provider_impl.h index b177d84..f5c2eb5 100644 --- a/services/device/generic_sensor/sensor_provider_impl.h +++ b/services/device/generic_sensor/sensor_provider_impl.h
@@ -7,6 +7,8 @@ #include "base/macros.h" #include "base/single_thread_task_runner.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/device/public/mojom/sensor_provider.mojom.h" namespace device { @@ -14,19 +16,18 @@ class PlatformSensorProvider; class PlatformSensor; -// Implementation of SensorProvider mojo interface. -// Uses PlatformSensorProvider singleton to create platform specific instances -// of PlatformSensor which are used by SensorImpl. +// Implementation of SensorProvider mojo interface. Owns an instance of +// PlatformSensorProvider singleton to create platform specific instances +// of PlatformSensor which are used by SensorImpl. A single instance of this +// class is owned by DeviceService. class SensorProviderImpl final : public mojom::SensorProvider { public: - static void Create( - mojom::SensorProviderRequest request); - + explicit SensorProviderImpl(std::unique_ptr<PlatformSensorProvider> provider); ~SensorProviderImpl() override; - private: - explicit SensorProviderImpl(PlatformSensorProvider* provider); + void Bind(mojom::SensorProviderRequest request); + private: // SensorProvider implementation. void GetSensor(mojom::SensorType type, GetSensorCallback callback) override; @@ -37,7 +38,9 @@ GetSensorCallback callback, scoped_refptr<PlatformSensor> sensor); - PlatformSensorProvider* provider_; + std::unique_ptr<PlatformSensorProvider> provider_; + mojo::BindingSet<mojom::SensorProvider> bindings_; + mojo::StrongBindingSet<mojom::Sensor> sensor_bindings_; base::WeakPtrFactory<SensorProviderImpl> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SensorProviderImpl);
diff --git a/services/identity/identity_accessor_impl.cc b/services/identity/identity_accessor_impl.cc index 9b86b2f..f1df1ba9 100644 --- a/services/identity/identity_accessor_impl.cc +++ b/services/identity/identity_accessor_impl.cc
@@ -10,7 +10,6 @@ #include "base/time/time.h" #include "components/signin/public/identity_manager/access_token_info.h" #include "google_apis/gaia/google_service_auth_error.h" -#include "services/identity/public/mojom/account.mojom.h" namespace identity { @@ -44,7 +43,8 @@ GetPrimaryAccountInfoCallback callback) { CoreAccountInfo account_info = identity_manager_->GetPrimaryAccountInfo(); AccountState account_state = GetStateOfAccount(account_info); - std::move(callback).Run(account_info, account_state); + std::move(callback).Run(account_info.account_id, account_info.gaia, + account_info.email, account_state); } void IdentityAccessorImpl::GetPrimaryAccountWhenAvailable( @@ -62,7 +62,8 @@ DCHECK(!account_info.account_id.empty()); DCHECK(!account_info.email.empty()); DCHECK(!account_info.gaia.empty()); - std::move(callback).Run(account_info, account_state); + std::move(callback).Run(account_info.account_id, account_info.gaia, + account_info.email, account_state); } void IdentityAccessorImpl::GetAccessToken(const CoreAccountId& account_id, @@ -115,7 +116,8 @@ DCHECK(!account_info->gaia.empty()); for (auto&& callback : primary_account_available_callbacks_) { - std::move(callback).Run(account_info.value(), account_state); + std::move(callback).Run(account_info->account_id, account_info->gaia, + account_info->email, account_state); } primary_account_available_callbacks_.clear(); }
diff --git a/services/identity/identity_accessor_impl_unittest.cc b/services/identity/identity_accessor_impl_unittest.cc index 3ba65d8..a672b68e7 100644 --- a/services/identity/identity_accessor_impl_unittest.cc +++ b/services/identity/identity_accessor_impl_unittest.cc
@@ -11,7 +11,6 @@ #include "services/identity/identity_service.h" #include "services/identity/public/cpp/account_state.h" #include "services/identity/public/cpp/scope_set.h" -#include "services/identity/public/mojom/account.mojom.h" #include "services/identity/public/mojom/constants.mojom.h" #include "services/identity/public/mojom/identity_accessor.mojom.h" #include "services/service_manager/public/cpp/binder_registry.h" @@ -44,9 +43,20 @@ void OnReceivedPrimaryAccountInfo( base::RepeatingClosure quit_closure, - const base::Optional<CoreAccountInfo>& account_info, + const base::Optional<CoreAccountId>& account_id, + const base::Optional<std::string>& gaia, + const base::Optional<std::string>& email, const AccountState& account_state) { - primary_account_info_ = account_info; + base::Optional<CoreAccountInfo> received_info; + + if (account_id) { + received_info = CoreAccountInfo(); + received_info->account_id = account_id.value(); + received_info->gaia = gaia.value(); + received_info->email = email.value(); + } + + primary_account_info_ = received_info; primary_account_state_ = account_state; quit_closure.Run(); } @@ -54,20 +64,17 @@ void OnPrimaryAccountAvailable(base::RepeatingClosure quit_closure, CoreAccountInfo* caller_account_info, AccountState* caller_account_state, - const CoreAccountInfo& account_info, + const CoreAccountId& account_id, + const std::string& gaia, + const std::string& email, const AccountState& account_state) { - *caller_account_info = account_info; + caller_account_info->account_id = account_id; + caller_account_info->gaia = gaia; + caller_account_info->email = email; *caller_account_state = account_state; quit_closure.Run(); } - void OnGotAccounts(base::RepeatingClosure quit_closure, - std::vector<mojom::AccountPtr>* output, - std::vector<mojom::AccountPtr> accounts) { - *output = std::move(accounts); - quit_closure.Run(); - } - void OnReceivedAccessToken(base::RepeatingClosure quit_closure, const base::Optional<std::string>& access_token, base::Time expiration_time,
diff --git a/services/identity/public/cpp/account_info.typemap b/services/identity/public/cpp/account_info.typemap deleted file mode 100644 index 7b6a947a..0000000 --- a/services/identity/public/cpp/account_info.typemap +++ /dev/null
@@ -1,19 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/identity/public/mojom/core_account_info.mojom" -public_headers = [ "//components/signin/core/browser/account_info.h" ] -traits_headers = - [ "//services/identity/public/cpp/core_account_info_mojom_traits.h" ] -sources = [ - "//services/identity/public/cpp/core_account_info_mojom_traits.cc", -] -public_deps = [ - # TODO(blundell): In the long term, any files from //components/signin that - # are exposed to consumers of the Identity Service should move to be part of - # the client library of the Identity Service. - "//components/signin/core/browser:shared", -] - -type_mappings = [ "identity.mojom.CoreAccountInfo=::CoreAccountInfo" ]
diff --git a/services/identity/public/cpp/core_account_info_mojom_traits.cc b/services/identity/public/cpp/core_account_info_mojom_traits.cc deleted file mode 100644 index b388afa..0000000 --- a/services/identity/public/cpp/core_account_info_mojom_traits.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/identity/public/cpp/core_account_info_mojom_traits.h" - -#include "services/identity/public/cpp/core_account_id_mojom_traits.h" - -namespace mojo { - -// static -bool StructTraits< - identity::mojom::CoreAccountInfo::DataView, - ::CoreAccountInfo>::Read(identity::mojom::CoreAccountInfo::DataView data, - ::CoreAccountInfo* out) { - CoreAccountId account_id; - std::string gaia; - std::string email; - - if (!data.ReadAccountId(&account_id) || !data.ReadGaia(&gaia) || - !data.ReadEmail(&email)) { - return false; - } - - out->account_id = account_id; - out->gaia = gaia; - out->email = email; - - return true; -} - -// static -bool StructTraits<identity::mojom::CoreAccountInfo::DataView, - ::CoreAccountInfo>::IsNull(const ::CoreAccountInfo& input) { - // Note that a CoreAccountInfo being null cannot be defined as - // CoreAccountInfo::IsEmpty(), as IsEmpty() verifies that *all* fields are - // empty, which is not enough to ensure that only valid (i.e. fully filled) - // CoreAccountInfo are passed. - return input.account_id.empty() || input.gaia.empty() || input.email.empty(); -} - -} // namespace mojo
diff --git a/services/identity/public/cpp/core_account_info_mojom_traits.h b/services/identity/public/cpp/core_account_info_mojom_traits.h deleted file mode 100644 index 85e4675..0000000 --- a/services/identity/public/cpp/core_account_info_mojom_traits.h +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_IDENTITY_PUBLIC_CPP_CORE_ACCOUNT_INFO_MOJOM_TRAITS_H_ -#define SERVICES_IDENTITY_PUBLIC_CPP_CORE_ACCOUNT_INFO_MOJOM_TRAITS_H_ - -#include <string> - -#include "components/signin/core/browser/account_info.h" -#include "services/identity/public/mojom/core_account_info.mojom.h" - -namespace mojo { - -template <> -struct StructTraits<identity::mojom::CoreAccountInfo::DataView, - ::CoreAccountInfo> { - static const CoreAccountId& account_id(const ::CoreAccountInfo& r) { - return r.account_id; - } - - static const std::string& gaia(const ::CoreAccountInfo& r) { return r.gaia; } - - static const std::string& email(const ::CoreAccountInfo& r) { - return r.email; - } - - static bool Read(identity::mojom::CoreAccountInfo::DataView data, - ::CoreAccountInfo* out); - - static bool IsNull(const ::CoreAccountInfo& input); - - static void SetToNull(::CoreAccountInfo* output) { - *output = CoreAccountInfo(); - } -}; - -} // namespace mojo - -#endif // SERVICES_IDENTITY_PUBLIC_CPP_CORE_ACCOUNT_INFO_MOJOM_TRAITS_H_
diff --git a/services/identity/public/cpp/typemaps.gni b/services/identity/public/cpp/typemaps.gni index f9ee1786..8f5a706 100644 --- a/services/identity/public/cpp/typemaps.gni +++ b/services/identity/public/cpp/typemaps.gni
@@ -1,5 +1,4 @@ typemaps = [ - "//services/identity/public/cpp/account_info.typemap", "//services/identity/public/cpp/account_state.typemap", "//services/identity/public/cpp/core_account_id.typemap", "//services/identity/public/cpp/google_service_auth_error.typemap",
diff --git a/services/identity/public/mojom/BUILD.gn b/services/identity/public/mojom/BUILD.gn index c5b758a..a4dc4fa 100644 --- a/services/identity/public/mojom/BUILD.gn +++ b/services/identity/public/mojom/BUILD.gn
@@ -6,10 +6,8 @@ mojom("mojom") { sources = [ - "account.mojom", "account_state.mojom", "core_account_id.mojom", - "core_account_info.mojom", "google_service_auth_error.mojom", "identity_accessor.mojom", "scope_set.mojom",
diff --git a/services/identity/public/mojom/account.mojom b/services/identity/public/mojom/account.mojom deleted file mode 100644 index 33e356e..0000000 --- a/services/identity/public/mojom/account.mojom +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module identity.mojom; - -import "services/identity/public/mojom/core_account_info.mojom"; -import "services/identity/public/mojom/account_state.mojom"; - -// Represents a user's Google account on this device. -struct Account { - // The details of the account. - CoreAccountInfo info; - - // The current state of the account on this device. - AccountState state; -};
diff --git a/services/identity/public/mojom/core_account_info.mojom b/services/identity/public/mojom/core_account_info.mojom deleted file mode 100644 index a08ce8bc..0000000 --- a/services/identity/public/mojom/core_account_info.mojom +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module identity.mojom; - -import "services/identity/public/mojom/core_account_id.mojom"; - -// Information about a specific Google account. A valid CoreAccountInfo will -// always have an account ID, gaia ID, and email address. -struct CoreAccountInfo { - // The account ID used by OAuth2TokenService. This is an opaque identifier - // that represents this account within Chrome. - CoreAccountId account_id; - // The GAIA ID corresponding to this account. - string gaia; - // The email address corresponding to this account. - string email; -};
diff --git a/services/identity/public/mojom/identity_accessor.mojom b/services/identity/public/mojom/identity_accessor.mojom index 8974cf6..7007a41 100644 --- a/services/identity/public/mojom/identity_accessor.mojom +++ b/services/identity/public/mojom/identity_accessor.mojom
@@ -5,29 +5,32 @@ module identity.mojom; import "mojo/public/mojom/base/time.mojom"; -import "services/identity/public/mojom/account.mojom"; import "services/identity/public/mojom/core_account_id.mojom"; -import "services/identity/public/mojom/core_account_info.mojom"; import "services/identity/public/mojom/account_state.mojom"; import "services/identity/public/mojom/google_service_auth_error.mojom"; import "services/identity/public/mojom/scope_set.mojom"; // Gives access to information about the user's Google accounts. interface IdentityAccessor { - // Returns the CoreAccountInfo for the Google account that serves as the - // user's primary account, or null if the user has no primary account (e.g., - // if they are not signed in). |account_state| gives the current state of the - // account (relevant only if |account_info| is non-null). - GetPrimaryAccountInfo() => (CoreAccountInfo? account_info, + // Returns the information for the Google account that serves as the + // user's primary account. |account_id|, |gaia|, and |email| will be null if + // the user has no primary account (e.g., if they are not signed in). + // |account_state| gives the current state of the account (relevant only if + // |account_id| is non-null). + GetPrimaryAccountInfo() => (CoreAccountId? account_id, + string? gaia, + string? email, AccountState account_state); - // Returns the CoreAccountInfo for the Google account that serves as the + // Returns the information for the Google account that serves as the // user's primary account once this account is available (i.e., the user is // signed in, a refresh token is available, and the refresh token is in a // non-error state). |account_state| gives the current state of the account. // Overlapping requests are permitted; all pending requests will be called // back when the primary account is available. - GetPrimaryAccountWhenAvailable() => (CoreAccountInfo account_info, + GetPrimaryAccountWhenAvailable() => (CoreAccountId account_id, + string gaia, + string email, AccountState account_state); // Returns an access token with the requested scopes for the given
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index d2b0d85..16a627d 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -13,6 +13,50 @@ "--bucket", "chromium-result-details", "--test-name", + "android_browsertests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "MMB29Q", + "device_os_type": "userdebug", + "device_type": "bullhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "android_browsertests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", "android_webview_unittests" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" @@ -14083,6 +14127,50 @@ "--bucket", "chromium-result-details", "--test-name", + "android_browsertests" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "device_os": "KTU84P", + "device_os_type": "userdebug", + "device_type": "hammerhead", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ] + }, + "test": "android_browsertests" + }, + { + "args": [ + "--gs-results-bucket=chromium-result-details", + "--recover-devices" + ], + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", "android_webview_unittests" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 2fcb2d4..7c9dc7e 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -28,9 +28,6 @@ 'Marshmallow Tablet Tester', # chromium.fyi.json 'android-code-coverage', - # chromium.memory.json - 'Android CFI', - 'android-asan', ], }, 'android_webview_unittests': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index ed546cb..5e3c39d 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1345,6 +1345,28 @@ ] } ], + "BufferingBytesConsumerDelay": [ + { + "platforms": [ + "windows", + "mac", + "chromeos", + "linux", + "android" + ], + "experiments": [ + { + "name": "Enabled", + "params": { + "milliseconds": "50" + }, + "enable_features": [ + "BufferingBytesConsumerDelay" + ] + } + ] + } + ], "CertDualVerificationTrial": [ { "platforms": [
diff --git a/third_party/android_build_tools/art/OWNERS b/third_party/android_build_tools/art/OWNERS index a31e1e5..440abd95 100644 --- a/third_party/android_build_tools/art/OWNERS +++ b/third_party/android_build_tools/art/OWNERS
@@ -1,5 +1,4 @@ agrieve@chromium.org jbudorick@chromium.org lizeb@chromium.org -mattcary@chromium.org mheikal@chomrium.org
diff --git a/third_party/arcore-android-sdk-client/BUILD.gn b/third_party/arcore-android-sdk-client/BUILD.gn new file mode 100644 index 0000000..7d018ac0 --- /dev/null +++ b/third_party/arcore-android-sdk-client/BUILD.gn
@@ -0,0 +1,12 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_aar_prebuilt("com_google_ar_core_java") { + aar_path = "core-partner_chrome-1.10.0.aar" + info_path = "core-partner_chrome-1.10.0.info" + extract_native_libraries = true + split_compat_class_names = [ "com/google/ar/core/InstallActivity" ] +}
diff --git a/third_party/arcore-android-sdk-client/LICENSE b/third_party/arcore-android-sdk-client/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/arcore-android-sdk-client/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
diff --git a/third_party/arcore-android-sdk-client/OWNERS b/third_party/arcore-android-sdk-client/OWNERS new file mode 100644 index 0000000..d9b8c27 --- /dev/null +++ b/third_party/arcore-android-sdk-client/OWNERS
@@ -0,0 +1,6 @@ +bialpio@chromium.org +ddorwin@chromium.org + +# TEAM: xr-dev@chromium.org +# COMPONENT: Internals>XR>AR +# OS: Android
diff --git a/third_party/arcore-android-sdk-client/README.chromium b/third_party/arcore-android-sdk-client/README.chromium new file mode 100644 index 0000000..9253d5e --- /dev/null +++ b/third_party/arcore-android-sdk-client/README.chromium
@@ -0,0 +1,26 @@ +Name: ARCore SDK client library for Chrome +Short Name: com.google.ar:core-partner_chrome +URL: https://developers.google.com/ar/develop/java/enable-arcore#dependencies +Version: 1.10 +License: Apache 2.0 +License File: LICENSE +Security Critical: yes + +Description: +The ARCore Android SDK provides augmented reality capabilities to Android +devices. This package contains partner version of ARCore SDK client specific +to Chrome. Since the partner version is not published to Maven (see package URL +for more context about dependencies), the .aar file is uploaded to CIPD. + +Googler update instructions - see: +https://goto.google.com/arcore-chrome-client-update + +Local Modifications: +Added files required for consumption in Chrome (LICENSE, OWNERS, BUILD.gn, +cipd.yaml, *.info). + +The LICENSE file is taken from + * https://www.apache.org/licenses/LICENSE-2.0.txt + +Changes: +2019-07-01 - Initial version.
diff --git a/third_party/arcore-android-sdk-client/cipd.yaml b/third_party/arcore-android-sdk-client/cipd.yaml new file mode 100644 index 0000000..a34a1131 --- /dev/null +++ b/third_party/arcore-android-sdk-client/cipd.yaml
@@ -0,0 +1,4 @@ +package: chromium/third_party/arcore-android-sdk-client +description: ARCore SDK client binaries. +data: + - file: core-partner_chrome-1.10.0.aar
diff --git a/third_party/arcore-android-sdk-client/core-partner_chrome-1.10.0.info b/third_party/arcore-android-sdk-client/core-partner_chrome-1.10.0.info new file mode 100644 index 0000000..781f29e2 --- /dev/null +++ b/third_party/arcore-android-sdk-client/core-partner_chrome-1.10.0.info
@@ -0,0 +1,14 @@ +# Generated by //build/android/gyp/aar.py +# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen". + +aidl = [ ] +assets = [ ] +has_classes_jar = true +has_native_libraries = true +has_proguard_flags = true +has_r_text_file = true +is_manifest_empty = false +native_libraries = [ "jni/arm64-v8a/libarcore_sdk_c.so", "jni/arm64-v8a/libarcore_sdk_jni.so", "jni/armeabi-v7a/libarcore_sdk_c.so", "jni/armeabi-v7a/libarcore_sdk_jni.so", "jni/x86/libarcore_sdk_c.so", "jni/x86/libarcore_sdk_jni.so", "jni/x86_64/libarcore_sdk_c.so", "jni/x86_64/libarcore_sdk_jni.so" ] +resources = [ "res/layout/__arcore_education.xml", "res/raw/keep.xml", "res/values-af/values.xml", "res/values-am/values.xml", "res/values-ar-rEG/values.xml", "res/values-ar-rSA/values.xml", "res/values-az/values.xml", "res/values-b+es+419/values.xml", "res/values-b+sr+Latn/values.xml", "res/values-be/values.xml", "res/values-bg/values.xml", "res/values-bn/values.xml", "res/values-bs/values.xml", "res/values-ca/values.xml", "res/values-cs/values.xml", "res/values-da/values.xml", "res/values-de-rAT/values.xml", "res/values-de-rCH/values.xml", "res/values-de/values.xml", "res/values-el/values.xml", "res/values-en-rAU/values.xml", "res/values-en-rCA/values.xml", "res/values-en-rGB/values.xml", "res/values-en-rIE/values.xml", "res/values-en-rSG/values.xml", "res/values-en-rXC/values.xml", "res/values-en-rZA/values.xml", "res/values-es-rAR/values.xml", "res/values-es-rBO/values.xml", "res/values-es-rCL/values.xml", "res/values-es-rCO/values.xml", "res/values-es-rCR/values.xml", "res/values-es-rDO/values.xml", "res/values-es-rEC/values.xml", "res/values-es-rGT/values.xml", "res/values-es-rHN/values.xml", "res/values-es-rMX/values.xml", "res/values-es-rNI/values.xml", "res/values-es-rPA/values.xml", "res/values-es-rPE/values.xml", "res/values-es-rPR/values.xml", "res/values-es-rPY/values.xml", "res/values-es-rSV/values.xml", "res/values-es-rUS/values.xml", "res/values-es-rUY/values.xml", "res/values-es-rVE/values.xml", "res/values-es/values.xml", "res/values-et/values.xml", "res/values-eu/values.xml", "res/values-fa/values.xml", "res/values-fi/values.xml", "res/values-fil/values.xml", "res/values-fr-rCA/values.xml", "res/values-fr-rCH/values.xml", "res/values-fr/values.xml", "res/values-gl/values.xml", "res/values-gsw/values.xml", "res/values-gu/values.xml", "res/values-he/values.xml", "res/values-hi/values.xml", "res/values-hr/values.xml", "res/values-hu/values.xml", "res/values-hy/values.xml", "res/values-id/values.xml", "res/values-in/values.xml", "res/values-is/values.xml", "res/values-it/values.xml", "res/values-iw/values.xml", "res/values-ja/values.xml", "res/values-ka/values.xml", "res/values-kk/values.xml", "res/values-km/values.xml", "res/values-kn/values.xml", "res/values-ko/values.xml", "res/values-ky/values.xml", "res/values-lo/values.xml", "res/values-lt/values.xml", "res/values-lv/values.xml", "res/values-mk/values.xml", "res/values-ml/values.xml", "res/values-mn/values.xml", "res/values-mo/values.xml", "res/values-ms/values.xml", "res/values-my/values.xml", "res/values-nb/values.xml", "res/values-ne/values.xml", "res/values-nl/values.xml", "res/values-no/values.xml", "res/values-pa/values.xml", "res/values-pl/values.xml", "res/values-pt-rBR/values.xml", "res/values-pt-rPT/values.xml", "res/values-pt/values.xml", "res/values-ro/values.xml", "res/values-ru/values.xml", "res/values-si/values.xml", "res/values-sk/values.xml", "res/values-sl/values.xml", "res/values-sq/values.xml", "res/values-sr/values.xml", "res/values-sv/values.xml", "res/values-sw/values.xml", "res/values-ta/values.xml", "res/values-te/values.xml", "res/values-th/values.xml", "res/values-tl/values.xml", "res/values-tr/values.xml", "res/values-uk/values.xml", "res/values-ur/values.xml", "res/values-uz/values.xml", "res/values-vi/values.xml", "res/values-zh-rCN/values.xml", "res/values-zh-rHK/values.xml", "res/values-zh-rTW/values.xml", "res/values-zh/values.xml", "res/values-zu/values.xml", "res/values/values.xml" ] +subjar_tuples = [ ] +subjars = [ ]
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index 155c344..86d23b9 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -343,5 +343,12 @@ const base::Feature kBlinkHeapUnifiedGCScheduling{ "BlinkHeapUnifiedGCScheduling", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables a delay before BufferingBytesConsumer begins reading from its +// underlying consumer when instantiated with CreateWithDelay(). +const base::Feature kBufferingBytesConsumerDelay{ + "BufferingBytesConsumerDelay", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::FeatureParam<int> kBufferingBytesConsumerDelayMilliseconds{ + &kBufferingBytesConsumerDelay, "milliseconds", 50}; + } // namespace features } // namespace blink
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h index 38306d75..ebc3fd8 100644 --- a/third_party/blink/public/common/features.h +++ b/third_party/blink/public/common/features.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_PUBLIC_COMMON_FEATURES_H_ #include "base/feature_list.h" +#include "base/metrics/field_trial_params.h" #include "third_party/blink/public/common/common_export.h" namespace blink { @@ -106,6 +107,10 @@ kBlinkHeapIncrementalMarkingStress; BLINK_COMMON_EXPORT extern const base::Feature kBlinkHeapUnifiedGCScheduling; +BLINK_COMMON_EXPORT extern const base::Feature kBufferingBytesConsumerDelay; +BLINK_COMMON_EXPORT extern const base::FeatureParam<int> + kBufferingBytesConsumerDelayMilliseconds; + } // namespace features } // namespace blink
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom index c2b391ec..c03e02e 100644 --- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -4,7 +4,7 @@ module blink.mojom; -const int32 kMaximumCSSSampleId = 643; +const int32 kMaximumCSSSampleId = 645; // This CSSSampleId represents page load for CSS histograms. It is recorded once // per page visit for each CSS histogram being logged on the blink side and the
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc index 8b42160..a5af0023 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc +++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.cc
@@ -28,7 +28,9 @@ } } -ScriptPromiseResolver::~ScriptPromiseResolver() { +ScriptPromiseResolver::~ScriptPromiseResolver() = default; + +void ScriptPromiseResolver::Dispose() { #if DCHECK_IS_ON() // This assertion fails if: // - promise() is called at least once and @@ -51,6 +53,9 @@ << create_stack_trace_.ToString(); } #endif + deferred_resolve_task_.Cancel(); + resolver_.Clear(); + value_.Clear(); } void ScriptPromiseResolver::Reject(ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h index affcf75..934967b 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h +++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h
@@ -39,16 +39,13 @@ : public GarbageCollectedFinalized<ScriptPromiseResolver>, public ContextLifecycleObserver { USING_GARBAGE_COLLECTED_MIXIN(ScriptPromiseResolver); + USING_PRE_FINALIZER(ScriptPromiseResolver, Dispose); public: explicit ScriptPromiseResolver(ScriptState*); virtual ~ScriptPromiseResolver(); -#if DCHECK_IS_ON() - // Eagerly finalized so as to ensure valid access to getExecutionContext() - // from the destructor's assert. - EAGERLY_FINALIZE(); -#endif + void Dispose(); // Anything that can be passed to toV8 can be passed to this function. template <typename T>
diff --git a/third_party/blink/renderer/core/css/css_numeric_literal_value.h b/third_party/blink/renderer/core/css/css_numeric_literal_value.h index 1988a91..cb1f0055 100644 --- a/third_party/blink/renderer/core/css/css_numeric_literal_value.h +++ b/third_party/blink/renderer/core/css/css_numeric_literal_value.h
@@ -18,6 +18,24 @@ CSSNumericLiteralValue(double num, UnitType type); + UnitType GetType() const { + return static_cast<UnitType>(numeric_literal_unit_type_); + } + + bool IsFontRelativeLength() const { + return GetType() == UnitType::kQuirkyEms || GetType() == UnitType::kEms || + GetType() == UnitType::kExs || GetType() == UnitType::kRems || + GetType() == UnitType::kChs; + } + bool IsQuirkyEms() const { return GetType() == UnitType::kQuirkyEms; } + bool IsViewportPercentageLength() const { + return CSSPrimitiveValue::IsViewportPercentageLength(GetType()); + } + bool IsResolution() const { + return CSSPrimitiveValue::IsResolution(GetType()); + } + bool IsFlex() const { return CSSPrimitiveValue::IsFlex(GetType()); } + bool IsZero() const { return !DoubleValue(); } double DoubleValue() const { return num_; }
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc index f5ed6bf6..344ac2f 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value.cc +++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -95,15 +95,37 @@ CSSPrimitiveValue::UnitType CSSPrimitiveValue::TypeWithCalcResolved() const { if (IsNumericLiteralValue()) - return GetType(); + return To<CSSNumericLiteralValue>(this)->GetType(); return To<CSSMathFunctionValue>(this)->TypeWithMathFunctionResolved(); } bool CSSPrimitiveValue::IsCalculatedPercentageWithLength() const { + // TODO(crbug.com/979895): Move this function to |CSSMathFunctionValue|. return IsCalculated() && To<CSSMathFunctionValue>(this)->Category() == kCalcPercentLength; } +bool CSSPrimitiveValue::IsFontRelativeLength() const { + // TODO(crbug.com/979895): Move this function to |CSSNumericLiteralValue|. + return IsNumericLiteralValue() && + To<CSSNumericLiteralValue>(this)->IsFontRelativeLength(); +} + +bool CSSPrimitiveValue::IsResolution() const { + // TODO(style-dev): Either support math functions on resolutions; or provide a + // justification for not supporting it, and move this function to + // |CSSNumericLiteralValue|. + return IsNumericLiteralValue() && + To<CSSNumericLiteralValue>(this)->IsResolution(); +} + +bool CSSPrimitiveValue::IsFlex() const { + // TODO(style-dev): Either support math functions on flexible lengths; or + // provide a justification for not supporting it, and move this function to + // |CSSNumericLiteralValue|. + return IsNumericLiteralValue() && To<CSSNumericLiteralValue>(this)->IsFlex(); +} + CSSPrimitiveValue::CSSPrimitiveValue(ClassType class_type) : CSSValue(class_type) {}
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h index a9f6d1f..4162932 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value.h +++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -160,15 +160,7 @@ unit == UnitType::kGradians || unit == UnitType::kTurns; } bool IsAngle() const { return IsAngle(TypeWithCalcResolved()); } - bool IsFontRelativeLength() const { - return GetType() == UnitType::kQuirkyEms || GetType() == UnitType::kEms || - GetType() == UnitType::kExs || GetType() == UnitType::kRems || - GetType() == UnitType::kChs; - } - bool IsQuirkyEms() const { return GetType() == UnitType::kQuirkyEms; } - bool IsViewportPercentageLength() const { - return IsViewportPercentageLength(GetType()); - } + bool IsFontRelativeLength() const; static bool IsViewportPercentageLength(UnitType type) { return type >= UnitType::kViewportWidth && type <= UnitType::kViewportMax; } @@ -203,9 +195,9 @@ return type >= UnitType::kDotsPerPixel && type <= UnitType::kDotsPerCentimeter; } - bool IsResolution() const { return IsResolution(GetType()); } + bool IsResolution() const; static bool IsFlex(UnitType unit) { return unit == UnitType::kFraction; } - bool IsFlex() const { return IsFlex(GetType()); } + bool IsFlex() const; // Creates either a |CSSNumericLiteralValue| or a |CSSMathFunctionValue|, // depending on whether |value| is calculated or not. We should never create a @@ -271,11 +263,6 @@ static UnitType StringToUnitType(const UChar*, unsigned length); double ComputeLengthDouble(const CSSToLengthConversionData&) const; - - // TODO(crbug.com/979895): This should be moved to CSSNumericLiteralValue - inline UnitType GetType() const { - return static_cast<UnitType>(numeric_literal_unit_type_); - } }; using CSSLengthArray = CSSPrimitiveValue::CSSLengthArray;
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index d1c31756..8ddcafb 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -2497,6 +2497,20 @@ type_name: "EOverflow", }, { + name: "overscroll-behavior-inline", + direction_aware_options: { + resolver: "inline", + physical_group: "overscroll-behavior", + }, + }, + { + name: "overscroll-behavior-block", + direction_aware_options: { + resolver: "block", + physical_group: "overscroll-behavior", + }, + }, + { name: "overscroll-behavior-x", property_methods: ["CSSValueFromComputedStyleInternal"], field_template: "keyword",
diff --git a/third_party/blink/renderer/core/css/local_font_face_source.cc b/third_party/blink/renderer/core/css/local_font_face_source.cc index 3240cb7..207ce12 100644 --- a/third_party/blink/renderer/core/css/local_font_face_source.cc +++ b/third_party/blink/renderer/core/css/local_font_face_source.cc
@@ -99,9 +99,6 @@ unstyled_description, font_name_, AlternateFontName::kLocalUniqueFace); histograms_.Record(font_data.get()); - if (font_data) { - LOG(ERROR) << "LOCAL FONT DATA CREATED FOR UNIQUE NAME: " << font_name_; - } return font_data; }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc index 3086876..5578a01c 100644 --- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc +++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -960,9 +960,9 @@ value_id == CSSValueID::kBreakWord; case CSSPropertyID::kScrollSnapStop: return value_id == CSSValueID::kNormal || value_id == CSSValueID::kAlways; + case CSSPropertyID::kOverscrollBehaviorInline: + case CSSPropertyID::kOverscrollBehaviorBlock: case CSSPropertyID::kOverscrollBehaviorX: - return value_id == CSSValueID::kAuto || - value_id == CSSValueID::kContain || value_id == CSSValueID::kNone; case CSSPropertyID::kOverscrollBehaviorY: return value_id == CSSValueID::kAuto || value_id == CSSValueID::kContain || value_id == CSSValueID::kNone; @@ -1020,6 +1020,8 @@ case CSSPropertyID::kPosition: case CSSPropertyID::kResize: case CSSPropertyID::kScrollBehavior: + case CSSPropertyID::kOverscrollBehaviorInline: + case CSSPropertyID::kOverscrollBehaviorBlock: case CSSPropertyID::kOverscrollBehaviorX: case CSSPropertyID::kOverscrollBehaviorY: case CSSPropertyID::kShapeRendering:
diff --git a/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.cc b/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.cc index 2cc62c8..451c66b 100644 --- a/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.cc +++ b/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.cc
@@ -80,6 +80,10 @@ return PhysicalGroup<2>(overflowShorthand()); } +PhysicalGroup<2> CSSDirectionAwareResolver::OverscrollBehaviorGroup() { + return PhysicalGroup<2>(overscrollBehaviorShorthand()); +} + PhysicalGroup<4> CSSDirectionAwareResolver::PaddingGroup() { return PhysicalGroup<4>(paddingShorthand()); }
diff --git a/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.h b/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.h index a7ac935c..4340a49 100644 --- a/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.h +++ b/third_party/blink/renderer/core/css/properties/css_direction_aware_resolver.h
@@ -40,6 +40,7 @@ static PhysicalGroup<2> MaxSizeGroup(); static PhysicalGroup<2> MinSizeGroup(); static PhysicalGroup<2> OverflowGroup(); + static PhysicalGroup<2> OverscrollBehaviorGroup(); static PhysicalGroup<4> PaddingGroup(); static PhysicalGroup<4> ScrollMarginGroup(); static PhysicalGroup<4> ScrollPaddingGroup();
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc index d6157ca..43229c18 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -1232,8 +1232,8 @@ const CSSValue& value) { Length length = ConvertLengthOrAuto(state, value); // This is only for margins which use __qem - auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value); - length.SetQuirk(primitive_value && primitive_value->IsQuirkyEms()); + auto* numeric_literal = DynamicTo<CSSNumericLiteralValue>(value); + length.SetQuirk(numeric_literal && numeric_literal->IsQuirkyEms()); return length; }
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc index 6625f089..3103559 100644 --- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc +++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
@@ -15,6 +15,19 @@ #include "third_party/blink/renderer/core/layout/layout_view.h" namespace blink { +namespace { + +// Returns the frame owner node for the frame that contains the given child, if +// one exists. Returns nullptr otherwise. +const Node* GetFrameOwnerNode(const Node* child) { + if (!child || !child->GetDocument().GetFrame() || + !child->GetDocument().GetFrame()->OwnerLayoutObject()) { + return nullptr; + } + return child->GetDocument().GetFrame()->OwnerLayoutObject()->GetNode(); +} + +} // namespace bool DisplayLockUtilities::ActivateFindInPageMatchRangeIfNeeded( const EphemeralRangeInFlatTree& range) { @@ -64,22 +77,28 @@ } DisplayLockUtilities::ScopedChainForcedUpdate::ScopedChainForcedUpdate( - const Node* node) { - if (!RuntimeEnabledFeatures::DisplayLockingEnabled() || - node->GetDocument().LockedDisplayLockCount() == 0) { + const Node* node, + bool include_self) { + if (!RuntimeEnabledFeatures::DisplayLockingEnabled()) return; - } + + CreateParentFrameScopeIfNeeded(node); + + if (node->GetDocument().LockedDisplayLockCount() == 0) + return; const_cast<Node*>(node)->UpdateDistributionForFlatTreeTraversal(); // Get the right ancestor view. Only use inclusive ancestors if the node // itself is locked and it prevents self layout. If self layout is not // prevented, we don't need to force the subtree layout, so use exclusive // ancestors in that case. - auto ancestor_view = [node] { + auto ancestor_view = [node, include_self] { if (auto* element = DynamicTo<Element>(node)) { auto* context = element->GetDisplayLockContext(); - if (context && !context->ShouldLayout(DisplayLockContext::kSelf)) + if (context && + (include_self || !context->ShouldLayout(DisplayLockContext::kSelf))) { return FlatTreeTraversal::InclusiveAncestorsOf(*node); + } } return FlatTreeTraversal::AncestorsOf(*node); }(); @@ -98,6 +117,15 @@ } } +void DisplayLockUtilities::ScopedChainForcedUpdate:: + CreateParentFrameScopeIfNeeded(const Node* node) { + auto* owner_node = GetFrameOwnerNode(node); + if (owner_node) { + parent_frame_scope_ = + std::make_unique<ScopedChainForcedUpdate>(owner_node, true); + } +} + const Element* DisplayLockUtilities::NearestLockedInclusiveAncestor( const Node& node) { auto* element = DynamicTo<Element>(node); @@ -187,26 +215,18 @@ return true; } - auto get_frame_owner_node = [](const Node* child) -> const Node* { - if (!child || !child->GetDocument().GetFrame() || - !child->GetDocument().GetFrame()->OwnerLayoutObject()) { - return nullptr; - } - return child->GetDocument().GetFrame()->OwnerLayoutObject()->GetNode(); - }; - // Since we handled the self-check above, we need to do inclusive checks // starting from the parent. node = FlatTreeTraversal::Parent(*node); // If we don't have a flat-tree parent, get the |source_node|'s owner node // instead. if (!node) - node = get_frame_owner_node(&source_node); + node = GetFrameOwnerNode(&source_node); while (node) { if (NearestLockedInclusiveAncestor(*node)) return true; - node = get_frame_owner_node(node); + node = GetFrameOwnerNode(node); } return false; }
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h index 70df6e8..9447447 100644 --- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h +++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
@@ -18,17 +18,20 @@ public: // This class forces updates on display locks from the given node up the - // ancestor chain until the root. + // ancestor chain until the local frame root. class ScopedChainForcedUpdate { - STACK_ALLOCATED(); DISALLOW_COPY_AND_ASSIGN(ScopedChainForcedUpdate); public: - explicit ScopedChainForcedUpdate(const Node* node); + explicit ScopedChainForcedUpdate(const Node* node, + bool include_self = false); ~ScopedChainForcedUpdate() = default; + void CreateParentFrameScopeIfNeeded(const Node* node); + private: Vector<DisplayLockContext::ScopedForcedUpdate> scoped_update_forced_list_; + std::unique_ptr<ScopedChainForcedUpdate> parent_frame_scope_; }; // Activates all the nodes within a find-in-page match |range|. // Returns true if at least one node gets activated.
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index e11a5d8..734ee52 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3624,36 +3624,6 @@ web_view_helper.Reset(); // Remove dependency on locally scoped client. } -class RedirectBlankToTopClient - : public ViewCreatingWebViewClient, - private ScopedRedirectBlankNavigationToTopForTest { - public: - RedirectBlankToTopClient() - : ScopedRedirectBlankNavigationToTopForTest(true) {} -}; - -TEST_F(WebViewTest, BlankRedirectsToTop) { - RedirectBlankToTopClient client; - frame_test_helpers::WebViewHelper web_view_helper; - WebViewImpl* web_view_impl = web_view_helper.Initialize(nullptr, &client); - WebLocalFrameImpl* frame = web_view_impl->MainFrameImpl(); - frame->SetName("_start"); - - // Make a request that will open a new window - WebURLRequest web_url_request(KURL("about:blank")); - FrameLoadRequest request(nullptr, web_url_request.ToResourceRequest()); - const FrameTree::FindResult& found_frame = - To<LocalFrame>(web_view_impl->GetPage()->MainFrame()) - ->Tree() - .FindOrCreateFrameForNavigation(request, "_blank"); - ASSERT_FALSE(client.CreatedWebView()); - WebLocalFrameImpl* found_local_frame = WebLocalFrameImpl::FromFrame( - DynamicTo<LocalFrame>(found_frame.frame.Get())); - EXPECT_EQ(found_local_frame, frame); - - web_view_helper.Reset(); // Remove dependency on locally scoped client. -} - class ViewReusingWebViewClient : public frame_test_helpers::TestWebViewClient { public: ViewReusingWebViewClient() = default;
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc index df97cd9..238429f 100644 --- a/third_party/blink/renderer/core/fetch/fetch_manager.cc +++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -480,13 +480,15 @@ if (fetch_request_data_->Integrity().IsEmpty() && !response_has_no_store_header_) { // BufferingBytesConsumer reads chunks from |bytes_consumer| as soon as - // they get available to relieve backpressure. + // they get available to relieve backpressure. Buffering starts after + // a short delay, however, to allow the Response to be drained; e.g. + // when the Response is passed to FetchEvent.respondWith(), etc. // // https://fetch.spec.whatwg.org/#fetching // The user agent should ignore the suspension request if the ongoing // fetch is updating the response in the HTTP cache for the request. - place_holder_body_->Update( - MakeGarbageCollected<BufferingBytesConsumer>(&body)); + place_holder_body_->Update(BufferingBytesConsumer::CreateWithDelay( + &body, GetExecutionContext()->GetTaskRunner(TaskType::kNetworking))); } else { place_holder_body_->Update(&body); }
diff --git a/third_party/blink/renderer/core/frame/use_counter_helper.cc b/third_party/blink/renderer/core/frame/use_counter_helper.cc index 4235a98..81225ea1 100644 --- a/third_party/blink/renderer/core/frame/use_counter_helper.cc +++ b/third_party/blink/renderer/core/frame/use_counter_helper.cc
@@ -1249,6 +1249,10 @@ return 642; case CSSPropertyID::kSyntax: return 643; + case CSSPropertyID::kOverscrollBehaviorInline: + return 644; + case CSSPropertyID::kOverscrollBehaviorBlock: + return 645; // 1. Add new features above this line (don't change the assigned numbers of // the existing items). // 2. Update kMaximumCSSSampleId (defined in
diff --git a/third_party/blink/renderer/core/input/mouse_event_manager.cc b/third_party/blink/renderer/core/input/mouse_event_manager.cc index 6049b4db..3808111c 100644 --- a/third_party/blink/renderer/core/input/mouse_event_manager.cc +++ b/third_party/blink/renderer/core/input/mouse_event_manager.cc
@@ -393,9 +393,10 @@ } void MouseEventManager::RecomputeMouseHoverStateIfNeeded() { + // |RecomputeMouseHoverState| may set |hover_state_dirty_| to be true. if (HoverStateDirty()) { - RecomputeMouseHoverState(); hover_state_dirty_ = false; + RecomputeMouseHoverState(); } }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc index 066ba20..bd50e65 100644 --- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc +++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -197,16 +197,17 @@ line_layout_item = LineLayoutItem(this); } - SECURITY_DCHECK(line_layout_item.IsLayoutInline() || - line_layout_item.IsEqual(this)); - - LineLayoutInline inline_flow( - !line_layout_item.IsEqual(this) ? line_layout_item : nullptr); - // Get the last box we made for this layout object. - parent_box = inline_flow - ? inline_flow.LastLineBox() - : LineLayoutBlockFlow(line_layout_item).LastLineBox(); + bool allowed_to_construct_new_box; + if (line_layout_item.IsLayoutInline()) { + LineLayoutInline inline_flow(line_layout_item); + parent_box = inline_flow.LastLineBox(); + allowed_to_construct_new_box = inline_flow.AlwaysCreateLineBoxes(); + } else { + DCHECK(line_layout_item.IsEqual(this)); + parent_box = LineLayoutBlockFlow(line_layout_item).LastLineBox(); + allowed_to_construct_new_box = true; + } // If this box or its ancestor is constructed then it is from a previous // line, and we need to make a new box for our line. If this box or its @@ -215,8 +216,6 @@ // inline has actually been split in two on the same line (this can happen // with very fancy language mixtures). bool constructed_new_box = false; - bool allowed_to_construct_new_box = - !inline_flow || inline_flow.AlwaysCreateLineBoxes(); bool can_use_existing_parent_box = parent_box && !ParentIsConstructedOrHaveNext(parent_box); if (allowed_to_construct_new_box && !can_use_existing_parent_box) {
diff --git a/third_party/blink/renderer/core/layout/layout_list_box.cc b/third_party/blink/renderer/core/layout/layout_list_box.cc index d949534..12c920ac 100644 --- a/third_party/blink/renderer/core/layout/layout_list_box.cc +++ b/third_party/blink/renderer/core/layout/layout_list_box.cc
@@ -83,7 +83,7 @@ return LayoutUnit(); const auto& items = select->GetListItems(); - if (items.IsEmpty()) + if (items.IsEmpty() || ShouldApplySizeContainment()) return DefaultItemHeight(); LayoutUnit max_height;
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc index edf85f3..556115f3 100644 --- a/third_party/blink/renderer/core/layout/layout_table_section.cc +++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -1920,7 +1920,8 @@ if (!child->IsText() && child->StyleRef().LogicalHeight().IsPercentOrCalc() && (flex_all_children || ShouldFlexCellChild(cell, child)) && - (!child->IsTable() || ToLayoutTable(child)->HasSections())) { + (!child->IsTable() || (!child->IsOutOfFlowPositioned() && + ToLayoutTable(child)->HasSections()))) { cell_children_flex = true; break; }
diff --git a/third_party/blink/renderer/core/page/frame_tree.cc b/third_party/blink/renderer/core/page/frame_tree.cc index f012ce9..b35e797e 100644 --- a/third_party/blink/renderer/core/page/frame_tree.cc +++ b/third_party/blink/renderer/core/page/frame_tree.cc
@@ -242,17 +242,10 @@ if (EqualIgnoringASCIICase(name, "_parent")) return Parent() ? Parent() : this_frame_.Get(); - if (EqualIgnoringASCIICase(name, "_blank")) { - if (RuntimeEnabledFeatures::RedirectBlankNavigationToTopEnabled()) { - // TODO(mthiesse): Only apply this behavior to navigations without - // rel=opener once - // https://html.spec.whatwg.org/multipage/links.html#link-type-opener is - // implemented. - return &Top(); - } else { - return nullptr; - } - } + // Since "_blank" should never be any frame's name, the following just amounts + // to an optimization. + if (EqualIgnoringASCIICase(name, "_blank")) + return nullptr; // Search subtree starting with this frame first. for (Frame* frame = this_frame_; frame;
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc index 322e727c..81cb9f09 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -65,9 +65,7 @@ return a->first_size > b->first_size; // This make sure that two different nodes with the same |first_size| wouldn't // be merged in the set. - if (a->node_id != b->node_id) - return a->node_id > b->node_id; - return a->record_id > b->record_id; + return a->insertion_index < b->insertion_index; } ImagePaintTimingDetector::ImagePaintTimingDetector(LocalFrameView* frame_view) @@ -315,11 +313,8 @@ const uint64_t& visual_size) { DCHECK(!RecordedTooManyNodes()); DCHECK_GT(visual_size, 0u); - std::unique_ptr<ImageRecord> record = std::make_unique<ImageRecord>(); - record->record_id = max_record_id_++; - record->node_id = node_id; - record->first_size = visual_size; - record->cached_image = cached_image; + std::unique_ptr<ImageRecord> record = + std::make_unique<ImageRecord>(node_id, cached_image, visual_size); return record; }
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h index 80069a5..d54612a 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -28,15 +28,27 @@ // TODO(crbug/960502): we should limit the access of these properties. class ImageRecord : public base::SupportsWeakPtr<ImageRecord> { public: - unsigned record_id; + ImageRecord(DOMNodeId new_node_id, + const ImageResourceContent* new_cached_image, + uint64_t new_first_size) + : node_id(new_node_id), + cached_image(new_cached_image), + first_size(new_first_size) { + static unsigned next_insertion_index_ = 1; + insertion_index = next_insertion_index_++; + } + + ImageRecord() {} + DOMNodeId node_id = kInvalidDOMNodeId; + WeakPersistent<const ImageResourceContent> cached_image; // Mind that |first_size| has to be assigned before pusing to // |size_ordered_set_| since it's the sorting key. uint64_t first_size = 0; unsigned frame_index = 0; + unsigned insertion_index; // The time of the first paint after fully loaded. 0 means not painted yet. base::TimeTicks paint_time = base::TimeTicks(); - WeakPersistent<const ImageResourceContent> cached_image; bool loaded = false; }; @@ -151,7 +163,6 @@ record->loaded = true; } - unsigned max_record_id_ = 0; HashMap<BackgroundImageId, std::unique_ptr<ImageRecord>> visible_background_image_map_; HashSet<DOMNodeId> invisible_node_ids_;
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc index 9a3af1a..be87f279 100644 --- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc +++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -235,6 +235,31 @@ EXPECT_TRUE(record->loaded); } +TEST_F(ImagePaintTimingDetectorTest, InsertionOrderIsSecondaryRankingKey) { + SetBodyInnerHTML(R"HTML( + )HTML"); + + auto* image1 = MakeGarbageCollected<HTMLImageElement>(GetDocument()); + image1->setAttribute("id", "image1"); + GetDocument().body()->AppendChild(image1); + SetImageAndPaint("image1", 5, 5); + + auto* image2 = MakeGarbageCollected<HTMLImageElement>(GetDocument()); + image2->setAttribute("id", "image2"); + GetDocument().body()->AppendChild(image2); + SetImageAndPaint("image2", 5, 5); + + auto* image3 = MakeGarbageCollected<HTMLImageElement>(GetDocument()); + image3->setAttribute("id", "image3"); + GetDocument().body()->AppendChild(image3); + SetImageAndPaint("image3", 5, 5); + + UpdateAllLifecyclePhasesAndInvokeCallbackIfAny(); + + EXPECT_EQ(FindLargestPaintCandidate()->node_id, + DOMNodeIds::ExistingIdForNode(image1)); +} + TEST_F(ImagePaintTimingDetectorTest, LargestImagePaint_TraceEvent_Candidate) { using trace_analyzer::Query; trace_analyzer::Start("loading");
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index f8a8831..5395153 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -1088,6 +1088,11 @@ current->child_needs_compositing_inputs_update_ = true; if (Compositor() && current->GetLayoutObject().ShouldApplyStrictContainment() && + // TODO(rego): Disable CompositingInputsRoot optimization if the + // "contain: strict" element has "position: sticky". This was causing + // crashes because PaintLayerScrollableArea::sticky_constraints_map_ was + // not updated correctly in some cases (see crbug.com/949887). + !current->GetLayoutObject().IsStickyPositioned() && // TODO(rego): Disable CompositingInputsRoot optimization for iframes // (see crbug.com/953159). !current->GetLayoutObject().IsLayoutIFrame())
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc index 382e99989..a9b55c2 100644 --- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc +++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -35,7 +35,7 @@ return a->first_size > b->first_size; // This make sure that two different nodes with the same |first_size| wouldn't // be merged in the set. - return a->node_id > b->node_id; + return a->insertion_index_ < b->insertion_index_; } } // namespace
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h index 22763a1d..5bf4bba 100644 --- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -32,10 +32,16 @@ const FloatRect& element_timing_rect) : node_id(new_node_id), first_size(new_first_size), - element_timing_rect_(element_timing_rect) {} + element_timing_rect_(element_timing_rect) { + static unsigned next_insertion_index_ = 1; + insertion_index_ = next_insertion_index_++; + } DOMNodeId node_id = kInvalidDOMNodeId; uint64_t first_size = 0; + // |insertion_index_| is ordered by insertion time, used as a secondary key + // for ranking. + unsigned insertion_index_ = 0; FloatRect element_timing_rect_; // The time of the first paint after fully loaded. base::TimeTicks paint_time = base::TimeTicks();
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc index e8b9756..61f511a 100644 --- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc +++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -197,6 +197,17 @@ DOMNodeIds::ExistingIdForNode(only_text)); } +TEST_F(TextPaintTimingDetectorTest, InsertionOrderIsSecondaryRankingKey) { + SetBodyInnerHTML(R"HTML( + )HTML"); + Element* first = AppendDivElementToBody("text"); + AppendDivElementToBody("text"); + AppendDivElementToBody("text"); + UpdateAllLifecyclePhasesAndSimulateSwapTime(); + EXPECT_EQ(TextRecordOfLargestTextPaint()->node_id, + DOMNodeIds::ExistingIdForNode(first)); +} + TEST_F(TextPaintTimingDetectorTest, LargestTextPaint_TraceEvent_Candidate) { using trace_analyzer::Query; trace_analyzer::Start("*");
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs index 086a565..45c3c72 100644 --- a/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs +++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/toast/index.mjs
@@ -75,6 +75,12 @@ // TODO(jacksteinberg): use https://github.com/whatwg/html/pull/4658 when implemented } + get action() { + return this.#actionSlot.assignedNodes().length !== 0 ? + this.#actionSlot.assignedNodes()[0] : + null; + } + show({duration = DEFAULT_DURATION} = {}) { this.setAttribute('open', ''); clearTimeout(this.#timeoutID);
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc index b0188383..e21bd225 100644 --- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc +++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -917,39 +917,30 @@ time_container_->NotifyIntervalsChanged(); } -SVGSMILElement::RestartedInterval SVGSMILElement::MaybeRestartInterval( +base::Optional<SMILInterval> SVGSMILElement::CheckForNewRestartInterval( double elapsed) { DCHECK(!is_waiting_for_first_interval_); DCHECK(elapsed >= interval_.begin); Restart restart = GetRestart(); if (restart == kRestartNever) - return kDidNotRestartInterval; + return base::nullopt; - if (elapsed < interval_.end) { - if (restart != kRestartAlways) - return kDidNotRestartInterval; + base::Optional<SMILInterval> modified; + if (elapsed < interval_.end && restart == kRestartAlways) { SMILTime next_begin = FindInstanceTime(kBegin, interval_.begin, false); if (next_begin < interval_.end) { - interval_.end = next_begin; - NotifyDependentsIntervalChanged(interval_); + modified = interval_; + modified->end = next_begin; } } - if (elapsed >= interval_.end) { - base::Optional<SMILInterval> next_interval = ResolveNextInterval(); - if (next_interval) { - interval_ = *next_interval; - NotifyDependentsIntervalChanged(interval_); - next_progress_time_ = - next_progress_time_.IsUnresolved() - ? interval_.begin - : std::min(next_progress_time_, interval_.begin); - if (elapsed >= interval_.begin) - return kDidRestartInterval; - } + if ((modified && elapsed >= modified->end) || + (!modified && elapsed >= interval_.end)) { + modified = ResolveNextInterval(); } - return kDidNotRestartInterval; + + return modified; } void SVGSMILElement::SeekToIntervalCorrespondingToTime(double elapsed) { @@ -1171,14 +1162,25 @@ float percent = CalculateAnimationPercent(elapsed); unsigned repeat = CalculateAnimationRepeat(elapsed); - bool restarted_interval = - MaybeRestartInterval(elapsed) == RestartedInterval::kDidRestartInterval; + + base::Optional<SMILInterval> new_interval = + CheckForNewRestartInterval(elapsed); + bool interval_did_restart = + (new_interval && elapsed >= (*new_interval).begin); + + if (new_interval) { + interval_ = *new_interval; + NotifyDependentsIntervalChanged(interval_); + next_progress_time_ = next_progress_time_.IsUnresolved() + ? interval_.begin + : std::min(next_progress_time_, interval_.begin); + } ActiveState old_active_state = GetActiveState(); active_state_ = DetermineActiveState(elapsed); if (IsContributing(elapsed)) { - if (old_active_state == kInactive || restarted_interval) { + if (old_active_state == kInactive || interval_did_restart) { ScheduleEvent(event_type_names::kBeginEvent); StartedActiveInterval(); } @@ -1191,7 +1193,7 @@ } if ((old_active_state == kActive && GetActiveState() != kActive) || - restarted_interval) { + interval_did_restart) { ScheduleEvent(event_type_names::kEndEvent); EndedActiveInterval(); }
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h index 3ed78ff..d5c47c3 100644 --- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h +++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
@@ -185,9 +185,7 @@ SMILTime resolved_end) const; SMILTime RepeatingDuration() const; - enum RestartedInterval { kDidNotRestartInterval, kDidRestartInterval }; - - RestartedInterval MaybeRestartInterval(double elapsed); + base::Optional<SMILInterval> CheckForNewRestartInterval(double elapsed); void BeginListChanged(SMILTime event_time); void EndListChanged(SMILTime event_time);
diff --git a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request_progress_event_throttle.h b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request_progress_event_throttle.h index ac02446..fdc728e 100644 --- a/third_party/blink/renderer/core/xmlhttprequest/xml_http_request_progress_event_throttle.h +++ b/third_party/blink/renderer/core/xmlhttprequest/xml_http_request_progress_event_throttle.h
@@ -52,6 +52,9 @@ class XMLHttpRequestProgressEventThrottle final : public GarbageCollectedFinalized<XMLHttpRequestProgressEventThrottle>, public TimerBase { + // Need to promptly stop this timer when it is deemed finalizable. + USING_PRE_FINALIZER(XMLHttpRequestProgressEventThrottle, Stop); + public: explicit XMLHttpRequestProgressEventThrottle(XMLHttpRequest*); ~XMLHttpRequestProgressEventThrottle() override; @@ -80,8 +83,6 @@ // depending on the value of the ProgressEventAction argument. void DispatchReadyStateChangeEvent(Event*, DeferredEventAction); - // Need to promptly stop this timer when it is deemed finalizable. - EAGERLY_FINALIZE(); void Trace(blink::Visitor*); private:
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc index 04506f3d..1b5f052 100644 --- a/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc +++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder.cc
@@ -172,11 +172,8 @@ "Execution context is detached."); return; } - DCHECK(stream_->getTracks().size()); recorder_handler_ = MediaRecorderHandler::Create( context->GetTaskRunner(TaskType::kInternalMediaRealTime)); - DCHECK(recorder_handler_); - if (!recorder_handler_) { exception_state.ThrowDOMException( DOMExceptionCode::kNotSupportedError, @@ -230,13 +227,21 @@ "The MediaRecorder's state is '" + StateToString(state_) + "'."); return; } + + if (stream_->getTracks().size() == 0) { + exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError, + "The MediaRecorder cannot start because" + "there are no audio or video tracks " + "available."); + return; + } + state_ = State::kRecording; if (!recorder_handler_->Start(time_slice)) { - exception_state.ThrowDOMException(DOMExceptionCode::kUnknownError, - "The MediaRecorder failed to start " - "because there are no audio or video " - "tracks available."); + exception_state.ThrowDOMException( + DOMExceptionCode::kUnknownError, + "There was an error starting the MediaRecorder."); return; } ScheduleDispatchEvent(Event::Create(event_type_names::kStart));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc index 602122b..ba9bbea 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -1724,12 +1724,20 @@ return ScriptPromise(); } + scoped_refptr<WebRTCICECandidate> web_candidate = ConvertToWebRTCIceCandidate( + ExecutionContext::From(script_state), candidate); + + // Temporary mitigation to avoid throwing an exception when candidate is + // empty. + // TODO(crbug.com/978582): Remove this mitigation when the WebRTC layer + // handles the empty candidate field correctly. + if (web_candidate->Candidate().IsEmpty()) + return ScriptPromise::CastUndefined(script_state); + auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); ScriptPromise promise = resolver->Promise(); auto* request = MakeGarbageCollected<RTCVoidRequestPromiseImpl>( base::nullopt, this, resolver, "RTCPeerConnection", "addIceCandidate"); - scoped_refptr<WebRTCICECandidate> web_candidate = ConvertToWebRTCIceCandidate( - ExecutionContext::From(script_state), candidate); bool implemented = peer_handler_->AddICECandidate(request, std::move(web_candidate)); if (!implemented) { @@ -1759,11 +1767,19 @@ return ScriptPromise(); } + scoped_refptr<WebRTCICECandidate> web_candidate = ConvertToWebRTCIceCandidate( + ExecutionContext::From(script_state), candidate); + + // Temporary mitigation to avoid throwing an exception when candidate is + // empty. + // TODO(crbug.com/978582): Remove this mitigation when the WebRTC layer + // handles the empty candidate field correctly. + if (web_candidate->Candidate().IsEmpty()) + return ScriptPromise::CastUndefined(script_state); + auto* request = MakeGarbageCollected<RTCVoidRequestImpl>( GetExecutionContext(), base::nullopt, this, success_callback, error_callback); - scoped_refptr<WebRTCICECandidate> web_candidate = ConvertToWebRTCIceCandidate( - ExecutionContext::From(script_state), candidate); bool implemented = peer_handler_->AddICECandidate(request, std::move(web_candidate)); if (!implemented) {
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h b/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h index 307e7bf..847e42e 100644 --- a/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h +++ b/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h
@@ -30,4 +30,4 @@ } // namespace blink -#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_INSPECTOR_HELPER_H_ +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_INSPECTOR_HELPER_MIXIN_H_
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc index 8fd4fe86..2f9b347 100644 --- a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc +++ b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.cc
@@ -33,6 +33,11 @@ const MIDIOptions* options) : ScriptPromiseResolver(script_state), options_(options) {} +void MIDIAccessInitializer::Dispose() { + accessor_.reset(); + permission_service_.reset(); +} + void MIDIAccessInitializer::ContextDestroyed(ExecutionContext* context) { accessor_.reset(); permission_service_.reset();
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h index 24f22af..c2a95ff1 100644 --- a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h +++ b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
@@ -24,6 +24,8 @@ class MODULES_EXPORT MIDIAccessInitializer : public ScriptPromiseResolver, public MIDIAccessorClient { + USING_PRE_FINALIZER(MIDIAccessInitializer, Dispose); + public: struct PortDescriptor { DISALLOW_NEW(); @@ -59,9 +61,7 @@ MIDIAccessInitializer(ScriptState*, const MIDIOptions*); ~MIDIAccessInitializer() override = default; - // Eager finalization to allow dispose() operation access - // other (non eager) heap objects. - EAGERLY_FINALIZE(); + void Dispose(); // MIDIAccessorClient void DidAddInputPort(const String& id,
diff --git a/third_party/blink/renderer/modules/webrtc/OWNERS b/third_party/blink/renderer/modules/webrtc/OWNERS new file mode 100644 index 0000000..c4d5a7d --- /dev/null +++ b/third_party/blink/renderer/modules/webrtc/OWNERS
@@ -0,0 +1,4 @@ +file://third_party/blink/renderer/modules/mediastream/OWNERS + +# TEAM: webrtc-dev@chromium.org +# COMPONENT: Blink>WebRTC
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc index 8ce59ef..dcb1b04 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -306,19 +306,29 @@ // offset. If the resulting width exceeds the available space the // preceding boundary is tried until the available space is sufficient. while (true) { - DCHECK_LE(first_safe, break_opportunity.offset); - last_safe = std::max( - result_->CachedPreviousSafeToBreakOffset(break_opportunity.offset), - first_safe); + DCHECK_LE(start, break_opportunity.offset); + last_safe = + result_->CachedPreviousSafeToBreakOffset(break_opportunity.offset); DCHECK_LE(last_safe, break_opportunity.offset); - DCHECK_GE(last_safe, first_safe); + // No need to reshape the line end because this opportunity is safe. if (last_safe == break_opportunity.offset) break; + + // Moved the opportunity back enough to require reshaping the whole line. + if (UNLIKELY(last_safe < first_safe)) { + DCHECK_LT(last_safe, start); + last_safe = start; + line_start_result = nullptr; + } + + // If previously determined to let it overflow, reshape the line end. DCHECK_LE(break_opportunity.offset, range_end); - if (is_overflow) { + if (UNLIKELY(is_overflow)) { line_end_result = Shape(last_safe, break_opportunity.offset); break; } + + // Check if this opportunity can fit after reshaping the line end. LayoutUnit safe_position = SnapStart( result_->CachedPositionForOffset(last_safe - range_start), direction); line_end_result = Shape(last_safe, break_opportunity.offset);
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc index bf8be1e..e2b2e0e 100644 --- a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc +++ b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc
@@ -74,55 +74,15 @@ painter_map_.erase(worklet_id); } -// TODO(xidachen): we should bundle all PaintWorkletInputs and send them to the -// |worklet_queue| once, instead of sending one PaintWorkletInput at a time. -// This avoids thread hop and boost performance. -sk_sp<cc::PaintRecord> PaintWorkletPaintDispatcher::Paint( - const cc::PaintWorkletInput* input) { - TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::Paint"); - sk_sp<cc::PaintRecord> output = sk_make_sp<cc::PaintOpBuffer>(); - - PaintWorkletPainterToTaskRunnerMap copied_painter_map = GetPainterMapCopy(); - if (copied_painter_map.IsEmpty()) - return output; - - base::WaitableEvent done_event; - - auto it = copied_painter_map.find(input->WorkletId()); - if (it == copied_painter_map.end()) - return output; - - PaintWorkletPainter* painter = it->value.first; - scoped_refptr<base::SingleThreadTaskRunner> task_runner = it->value.second; - DCHECK(!task_runner->BelongsToCurrentThread()); - std::unique_ptr<AutoSignal> done = std::make_unique<AutoSignal>(&done_event); - - PostCrossThreadTask( - *task_runner, FROM_HERE, - CrossThreadBindOnce( - [](PaintWorkletPainter* painter, const cc::PaintWorkletInput* input, - std::unique_ptr<AutoSignal> completion, - sk_sp<cc::PaintRecord>* output) { - *output = painter->Paint(input); - }, - WrapCrossThreadPersistent(painter), WTF::CrossThreadUnretained(input), - WTF::Passed(std::move(done)), WTF::CrossThreadUnretained(&output))); - - done_event.Wait(); - - // If the paint fails, PaintWorkletPainter should return an empty record - // rather than a nullptr. - DCHECK(output); - - return output; -} - void PaintWorkletPaintDispatcher::DispatchWorklets( cc::PaintWorkletJobMap worklet_job_map, PlatformPaintWorkletLayerPainter::DoneCallback done_callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::DispatchWorklets"); + // We must be called with a valid callback to guarantee our internal state. + DCHECK(!done_callback.is_null()); + // Dispatching to the worklets is an asynchronous process, but there should // only be one dispatch going on at once. We store the completion callback and // the PaintWorklet job map in the class during the dispatch, then clear them @@ -187,6 +147,11 @@ } } +bool PaintWorkletPaintDispatcher::HasOngoingDispatch() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return !on_async_paint_complete_.is_null(); +} + void PaintWorkletPaintDispatcher::AsyncPaintDone() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::AsyncPaintDone");
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h index f6db40b..a53f911 100644 --- a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h +++ b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h
@@ -42,11 +42,6 @@ PaintWorkletPaintDispatcher(); - // Dispatches a single paint class instance - represented by a - // PaintWorkletInput - to the appropriate PaintWorklet thread, and blocks - // until it receives the result. - sk_sp<cc::PaintRecord> Paint(const cc::PaintWorkletInput*); - // Dispatches a set of paint class instances - each represented by a // PaintWorkletInput - to the appropriate PaintWorklet threads, asynchronously // returning the results on the calling thread via the passed callback. @@ -56,6 +51,11 @@ void DispatchWorklets(cc::PaintWorkletJobMap, PlatformPaintWorkletLayerPainter::DoneCallback); + // Reports whether or not there is an ongoing dispatch (e.g. a set of + // PaintWorklet instances have been dispatched to the worklet, but the results + // have not yet been received.) + bool HasOngoingDispatch() const; + // Register and unregister a PaintWorklet (represented in this context by a // PaintWorkletPainter). A given PaintWorklet is registered once all its // global scopes have been created, and is usually only unregistered when the
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher_test.cc b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher_test.cc index cc0556e..61ee922 100644 --- a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher_test.cc +++ b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher_test.cc
@@ -80,7 +80,7 @@ map[worklet_id] = base::MakeRefCounted<cc::PaintWorkletJobVector>(); auto input = base::MakeRefCounted<MockPaintWorkletInput>(worklet_id); MockPaintWorkletInput* input_ptr = input.get(); - map[worklet_id]->data.emplace_back(std::move(input)); + map[worklet_id]->data.emplace_back(/*layer_id=*/1, std::move(input)); return input_ptr; } } // namespace @@ -238,4 +238,28 @@ WaitForTestCompletion(); } +TEST_F(PaintWorkletPaintDispatcherAsyncTest, + HasOngoingDispatchIsTrackedCorrectly) { + auto dispatcher = base::MakeRefCounted<PaintWorkletPaintDispatcher>(); + + const int first_worklet_id = 2; + auto* first_mock_painter = + MakeGarbageCollected<NiceMock<MockPaintWorkletPainter>>(first_worklet_id); + std::unique_ptr<Thread> first_thread = CreateTestThread("WorkletThread1"); + dispatcher->RegisterPaintWorkletPainter(first_mock_painter, + first_thread->GetTaskRunner()); + + // Nothing going on; no dispatch. + EXPECT_FALSE(dispatcher->HasOngoingDispatch()); + + cc::PaintWorkletJobMap job_map; + AddPaintWorkletInputToMap(job_map, first_worklet_id); + + dispatcher->DispatchWorklets(job_map, CreateTestCompleteCallback()); + EXPECT_TRUE(dispatcher->HasOngoingDispatch()); + + WaitForTestCompletion(); + EXPECT_FALSE(dispatcher->HasOngoingDispatch()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.cc b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.cc index 64567632..f989283 100644 --- a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.cc +++ b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.cc
@@ -26,11 +26,6 @@ "PlatformPaintWorkletLayerPainter::~PlatformPaintWorkletLayerPainter"); } -sk_sp<PaintRecord> PlatformPaintWorkletLayerPainter::Paint( - const cc::PaintWorkletInput* input) { - return dispatcher_->Paint(input); -} - void PlatformPaintWorkletLayerPainter::DispatchWorklets( cc::PaintWorkletJobMap worklet_data_map, DoneCallback done_callback) { @@ -38,4 +33,8 @@ std::move(done_callback)); } +bool PlatformPaintWorkletLayerPainter::HasOngoingDispatch() const { + return dispatcher_->HasOngoingDispatch(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h index c31745d..a8702b6 100644 --- a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h +++ b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h
@@ -29,8 +29,8 @@ ~PlatformPaintWorkletLayerPainter() override; // cc::PaintWorkletLayerPainter - sk_sp<cc::PaintRecord> Paint(const cc::PaintWorkletInput*) override; void DispatchWorklets(cc::PaintWorkletJobMap, DoneCallback) override; + bool HasOngoingDispatch() const override; private: scoped_refptr<PaintWorkletPaintDispatcher> dispatcher_;
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc index 7c86d73..70ab85c 100644 --- a/third_party/blink/renderer/platform/heap/heap.cc +++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -428,6 +428,12 @@ arenas_[i]->CompleteSweep(); } +void ThreadHeap::InvokeFinalizersOnSweptPages() { + for (size_t i = BlinkGC::kEagerSweepArenaIndex + 1; + i < BlinkGC::kNumberOfArenas; i++) + arenas_[i]->InvokeFinalizersOnSweptPages(); +} + void ThreadHeap::ClearArenaAges() { memset(arena_ages_, 0, sizeof(size_t) * BlinkGC::kNumberOfArenas); memset(likely_to_be_promptly_freed_.get(), 0,
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h index 1956db1..6d4cb10a 100644 --- a/third_party/blink/renderer/platform/heap/heap.h +++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -366,6 +366,7 @@ void PrepareForSweep(); void RemoveAllPages(); + void InvokeFinalizersOnSweptPages(); void CompleteSweep(); enum SnapshotType { kHeapSnapshot, kFreelistSnapshot };
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc index 3d29a279..e2fcff4 100644 --- a/third_party/blink/renderer/platform/heap/heap_page.cc +++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -123,7 +123,6 @@ DCHECK(unswept_pages_.IsEmpty()); DCHECK(swept_unfinalized_pages_.IsEmpty()); DCHECK(swept_unfinalized_empty_pages_.IsEmpty()); - DCHECK(SweepingAndFinalizationCompleted()); } void BaseArena::RemoveAllPages() { @@ -356,6 +355,16 @@ return true; } +void BaseArena::InvokeFinalizersOnSweptPages() { + while (BasePage* page = swept_unfinalized_pages_.PopLocked()) { + swept_pages_.PushLocked(page); + page->FinalizeSweep(SweepResult::kPageNotEmpty); + } + while (BasePage* page = swept_unfinalized_empty_pages_.PopLocked()) { + page->FinalizeSweep(SweepResult::kPageEmpty); + } +} + void BaseArena::SweepOnConcurrentThread() { while (BasePage* page = unswept_pages_.PopLocked()) { SweepUnsweptPageOnConcurrentThread(page); @@ -370,17 +379,13 @@ // Some phases, e.g. verification, require iterability of a page. MakeIterable(); + // First, sweep and finalize pages. while (BasePage* page = unswept_pages_.PopLocked()) { SweepUnsweptPage(page); } - while (BasePage* page = swept_unfinalized_pages_.PopLocked()) { - swept_pages_.PushLocked(page); - page->FinalizeSweep(SweepResult::kPageNotEmpty); - } - while (BasePage* page = swept_unfinalized_empty_pages_.PopLocked()) { - page->FinalizeSweep(SweepResult::kPageEmpty); - } + // Then, finalize pages that have been processed by concurrent sweepers. + InvokeFinalizersOnSweptPages(); // Verify object start bitmap after all freelists have been merged. VerifyObjectStartBitmap();
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h index 3b0fe93b..98fc6f41 100644 --- a/third_party/blink/renderer/platform/heap/heap_page.h +++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -847,6 +847,7 @@ bool LazySweepWithDeadline(base::TimeTicks deadline); void CompleteSweep(); void SweepOnConcurrentThread(); + void InvokeFinalizersOnSweptPages(); ThreadState* GetThreadState() { return thread_state_; } int ArenaIndex() const { return index_; }
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc index 57c1a6f..5903335f 100644 --- a/third_party/blink/renderer/platform/heap/heap_stats_collector.cc +++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.cc
@@ -183,6 +183,11 @@ scope_data[kUnifiedAtomicMarkingTransitiveClosure]; } +base::TimeDelta ThreadHeapStatsCollector::Event::atomic_sweep_and_compact_time() + const { + return scope_data[ThreadHeapStatsCollector::kAtomicSweepAndCompact]; +} + base::TimeDelta ThreadHeapStatsCollector::Event::marking_time() const { return incremental_marking_time() + atomic_marking_time(); } @@ -196,13 +201,18 @@ base::TimeDelta ThreadHeapStatsCollector::Event::gc_cycle_time() const { // Note that scopes added here also have to have a proper BlinkGCInV8Scope // scope if they are nested in a V8 scope. - return marking_time() + + return incremental_marking_time() + atomic_marking_time() + + atomic_sweep_and_compact_time() + scope_data[ThreadHeapStatsCollector::kAtomicSweepAndCompact] + scope_data[ThreadHeapStatsCollector::kCompleteSweep] + scope_data[ThreadHeapStatsCollector::kLazySweepInIdle] + scope_data[ThreadHeapStatsCollector::kLazySweepOnAllocation]; } +base::TimeDelta ThreadHeapStatsCollector::Event::atomic_pause_time() const { + return atomic_marking_time() + atomic_sweep_and_compact_time(); +} + base::TimeDelta ThreadHeapStatsCollector::Event::foreground_sweeping_time() const { return scope_data[kCompleteSweep] + scope_data[kEagerSweep] +
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/heap_stats_collector.h index e230ba3c..d782196 100644 --- a/third_party/blink/renderer/platform/heap/heap_stats_collector.h +++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
@@ -243,12 +243,18 @@ // sweeping time. base::TimeDelta gc_cycle_time() const; + // Time spent in the final atomic pause of a GC cycle. + base::TimeDelta atomic_pause_time() const; + + // Time spent in the final atomic pause for marking the heap. + base::TimeDelta atomic_marking_time() const; + + // Time spent in the final atomic pause in sweeping and compacting the heap. + base::TimeDelta atomic_sweep_and_compact_time() const; + // Time spent incrementally marking the heap. base::TimeDelta incremental_marking_time() const; - // Time spent in atomically marking the heap. - base::TimeDelta atomic_marking_time() const; - // Overall time spent marking the heap. base::TimeDelta marking_time() const;
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc index 1adb7d9a..a06136d 100644 --- a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc +++ b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
@@ -271,7 +271,7 @@ stats_collector.previous().marking_time().InMillisecondsF()); } -TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsFromFullGC) { +TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsStandAloneGC) { ThreadHeapStatsCollector stats_collector; stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); stats_collector.IncreaseScopeTime( @@ -283,6 +283,72 @@ stats_collector.previous().marking_time().InMillisecondsF()); } +TEST(ThreadHeapStatsCollectorTest, EventAtomicMarkingTimeStandAlone) { + ThreadHeapStatsCollector stats_collector; + stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kStandAloneAtomicMarking, + base::TimeDelta::FromMilliseconds(11)); + stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); + stats_collector.NotifySweepingCompleted(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(11), + stats_collector.previous().atomic_marking_time()); +} + +TEST(ThreadHeapStatsCollectorTest, EventAtomicMarkingTimeUnifiedHeapGC) { + ThreadHeapStatsCollector stats_collector; + stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingPrologue, + base::TimeDelta::FromMilliseconds(5)); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingTransitiveClosure, + base::TimeDelta::FromMilliseconds(3)); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingEpilogue, + base::TimeDelta::FromMilliseconds(1)); + stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); + stats_collector.NotifySweepingCompleted(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(9), + stats_collector.previous().atomic_marking_time()); +} + +TEST(ThreadHeapStatsCollectorTest, EventAtomicPauseTimeStandAlone) { + ThreadHeapStatsCollector stats_collector; + stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kStandAloneAtomicMarking, + base::TimeDelta::FromMilliseconds(17)); + stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kAtomicSweepAndCompact, + base::TimeDelta::FromMilliseconds(15)); + stats_collector.NotifySweepingCompleted(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(32), + stats_collector.previous().atomic_pause_time()); +} + +TEST(ThreadHeapStatsCollectorTest, EventAtomicPauseTimeUnifiedHeapGC) { + ThreadHeapStatsCollector stats_collector; + stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingPrologue, + base::TimeDelta::FromMilliseconds(5)); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingTransitiveClosure, + base::TimeDelta::FromMilliseconds(3)); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kUnifiedAtomicMarkingEpilogue, + base::TimeDelta::FromMilliseconds(1)); + stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); + stats_collector.IncreaseScopeTime( + ThreadHeapStatsCollector::kAtomicSweepAndCompact, + base::TimeDelta::FromMilliseconds(15)); + stats_collector.NotifySweepingCompleted(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(24), + stats_collector.previous().atomic_pause_time()); +} + TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) { ThreadHeapStatsCollector stats_collector; stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); @@ -298,6 +364,7 @@ TEST(ThreadHeapStatsCollectorTest, EventSweepingTimeInMs) { ThreadHeapStatsCollector stats_collector; stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting); + stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle, base::TimeDelta::FromMilliseconds(1)); stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle, @@ -309,7 +376,6 @@ base::TimeDelta::FromMilliseconds(4)); stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kCompleteSweep, base::TimeDelta::FromMilliseconds(5)); - stats_collector.NotifyMarkingCompleted(kNoMarkedBytes); stats_collector.NotifySweepingCompleted(); EXPECT_EQ(base::TimeDelta::FromMilliseconds(15), stats_collector.previous().sweeping_time()); @@ -446,6 +512,10 @@ stats_collector.previous().allocated_space_in_bytes_before_sweeping); } +// ============================================================================= +// ThreadHeapStatsObserver. ==================================================== +// ============================================================================= + namespace { class MockThreadHeapStatsObserver : public ThreadHeapStatsObserver {
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc index eb73cfd..9b09a66 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.cc +++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -642,8 +642,7 @@ } if (sweep_completed) { - // TODO(bikineev): We need to synchronize with concurrent sweepers here - // using the same bottleneck as in CompleteSweep(). + SynchronizeAndFinishConcurrentSweeping(); PostSweep(); } } @@ -950,15 +949,7 @@ "forced", current_gc_data_.reason == BlinkGC::GCReason::kForcedGCForTesting); Heap().CompleteSweep(); - - // Wait for concurrent sweepers. - sweeper_scheduler_.CancelAndWait(); - - // Concurrent sweepers may perform some work at the last stage (e.g. - // sweeping the last page and preparing finalizers). - // TODO(bikineev): This should be changed to Heap.Finalize() to only call - // remaining finalizers, not perform complete sweeping once again. - Heap().CompleteSweep(); + SynchronizeAndFinishConcurrentSweeping(); if (!was_in_atomic_pause) LeaveAtomicPause(); @@ -966,6 +957,18 @@ PostSweep(); } +void ThreadState::SynchronizeAndFinishConcurrentSweeping() { + DCHECK(CheckThread()); + DCHECK(IsSweepingInProgress()); + + // Wait for concurrent sweepers. + sweeper_scheduler_.CancelAndWait(); + + // Concurrent sweepers may perform some work at the last stage (e.g. + // sweeping the last page and preparing finalizers). + Heap().InvokeFinalizersOnSweptPages(); +} + BlinkGCObserver::BlinkGCObserver(ThreadState* thread_state) : thread_state_(thread_state) { thread_state_->AddObserver(this); @@ -1036,6 +1039,9 @@ void UpdateHistograms(const ThreadHeapStatsCollector::Event& event) { UMA_HISTOGRAM_ENUMERATION("BlinkGC.GCReason", event.reason); + UMA_HISTOGRAM_TIMES("BlinkGC.TimeForAtomicPhase", event.atomic_pause_time()); + UMA_HISTOGRAM_TIMES("BlinkGC.TimeForAtomicPhaseMarking", + event.atomic_marking_time()); UMA_HISTOGRAM_TIMES("BlinkGC.TimeForGCCycle", event.gc_cycle_time()); UMA_HISTOGRAM_TIMES("BlinkGC.TimeForIncrementalMarking", event.incremental_marking_time()); @@ -1049,7 +1055,6 @@ UMA_HISTOGRAM_TIMES( "BlinkGC.TimeForCompleteSweep", event.scope_data[ThreadHeapStatsCollector::kCompleteSweep]); - UMA_HISTOGRAM_TIMES( "BlinkGC.TimeForInvokingPreFinalizers", event.scope_data[ThreadHeapStatsCollector::kInvokePreFinalizers]); @@ -1074,11 +1079,6 @@ main_thread_marking_throughput_mb_per_s); } - // TODO(mlippautz): Convert the following histograms to "TimeFor..." notation. - - UMA_HISTOGRAM_TIMES("BlinkGC.AtomicPhaseMarking", - event.atomic_marking_time()); - DEFINE_STATIC_LOCAL( CustomCountHistogram, object_size_freed_by_heap_compaction, ("BlinkGC.ObjectSizeFreedByHeapCompaction", 1, 4 * 1024 * 1024, 50));
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h index 24bee1a..1d16ed6 100644 --- a/third_party/blink/renderer/platform/heap/thread_state.h +++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -533,6 +533,8 @@ void EagerSweep(); + void SynchronizeAndFinishConcurrentSweeping(); + void InvokePreFinalizers(); void ReportMemoryToV8();
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc index c89f4d4f..1b44c657 100644 --- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc +++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
@@ -4,22 +4,72 @@ #include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h" +#include "base/feature_list.h" +#include "base/metrics/field_trial_params.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { -BufferingBytesConsumer::BufferingBytesConsumer(BytesConsumer* bytes_consumer) - : bytes_consumer_(bytes_consumer) { +// static +BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelay( + BytesConsumer* bytes_consumer, + scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner) { + if (!base::FeatureList::IsEnabled(features::kBufferingBytesConsumerDelay)) + return Create(bytes_consumer); + + return MakeGarbageCollected<BufferingBytesConsumer>( + bytes_consumer, std::move(timer_task_runner), + base::TimeDelta::FromMilliseconds( + features::kBufferingBytesConsumerDelayMilliseconds.Get())); +} + +// static +BufferingBytesConsumer* BufferingBytesConsumer::Create( + BytesConsumer* bytes_consumer) { + return MakeGarbageCollected<BufferingBytesConsumer>(bytes_consumer, nullptr, + base::TimeDelta()); +} + +BufferingBytesConsumer::BufferingBytesConsumer( + BytesConsumer* bytes_consumer, + scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner, + base::TimeDelta buffering_start_delay) + : bytes_consumer_(bytes_consumer), + timer_(std::move(timer_task_runner), + this, + &BufferingBytesConsumer::OnTimerFired) { bytes_consumer_->SetClient(this); - BufferData(); + if (buffering_start_delay.is_zero()) { + MaybeStartBuffering(); + return; + } + timer_.StartOneShot(buffering_start_delay, FROM_HERE); } BufferingBytesConsumer::~BufferingBytesConsumer() = default; +void BufferingBytesConsumer::MaybeStartBuffering() { + if (buffering_state_ != BufferingState::kDelayed) + return; + timer_.Stop(); + buffering_state_ = BufferingState::kStarted; + BufferData(); +} + +void BufferingBytesConsumer::StopBuffering() { + timer_.Stop(); + buffering_state_ = BufferingState::kStopped; +} + BytesConsumer::Result BufferingBytesConsumer::BeginRead(const char** buffer, size_t* available) { + // Stop delaying buffering on the first read as it will no longer be safe to + // drain the underlying |bytes_consumer_| anyway. + MaybeStartBuffering(); + if (buffer_.IsEmpty()) { - if (!is_buffering_) + if (buffering_state_ != BufferingState::kStarted) return bytes_consumer_->BeginRead(buffer, available); if (has_seen_error_) @@ -47,7 +97,7 @@ BytesConsumer::Result BufferingBytesConsumer::EndRead(size_t read_size) { if (buffer_.IsEmpty()) { - if (!is_buffering_) + if (buffering_state_ != BufferingState::kStarted) return bytes_consumer_->EndRead(read_size); DCHECK(has_seen_error_); @@ -79,7 +129,7 @@ } mojo::ScopedDataPipeConsumerHandle BufferingBytesConsumer::DrainAsDataPipe() { - if (!is_buffering_) + if (buffering_state_ != BufferingState::kStarted) return bytes_consumer_->DrainAsDataPipe(); // We intentionally return an empty handle here, because returning a DataPipe @@ -117,6 +167,10 @@ BytesConsumer::Client::Trace(visitor); } +void BufferingBytesConsumer::OnTimerFired(TimerBase*) { + MaybeStartBuffering(); +} + void BufferingBytesConsumer::OnStateChange() { BytesConsumer::Client* client = client_; BufferData(); @@ -125,7 +179,7 @@ } void BufferingBytesConsumer::BufferData() { - if (!is_buffering_) + if (buffering_state_ != BufferingState::kStarted) return; while (true) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h index bb59397..2f419da 100644 --- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h +++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h" #include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/wtf/deque.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -30,17 +31,39 @@ USING_GARBAGE_COLLECTED_MIXIN(BufferingBytesConsumer); public: - // Creates a BufferingBytesConsumer. |bytes_consumer| is the original - // BytesConsumer. - // |bytes_consumer| must not have a client. - explicit BufferingBytesConsumer(BytesConsumer* bytes_consumer); + // Creates a BufferingBytesConsumer that waits some delay before beginning + // to buffer data from the underlying consumer. This delay provides an + // opportunity for the data to be drained before buffering begins. The + // |bytes_consumer| is the original BytesConsumer. |bytes_consumer| must + // not have a client. + static BufferingBytesConsumer* CreateWithDelay( + BytesConsumer* bytes_consumer, + scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner); + + // Creates a BufferingBytesConsumer that buffers immediately without any + // delay. |bytes_consumer| is the original BytesConsumer. |bytes_consumer| + // must not have a client. + static BufferingBytesConsumer* Create(BytesConsumer* bytes_consumer); + + // Use the Create*() factory methods instead of direct instantiation. + // TODO(crbug/954442): Use util::PassKey to prevent external callers. + BufferingBytesConsumer( + BytesConsumer* bytes_consumer, + scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner, + base::TimeDelta buffering_start_delay); ~BufferingBytesConsumer() override; + // Attempt to start buffering data from the underlying consumer. This will + // only have an effect if we're currently in the kDelayed state. If + // buffering has already started or been explicitly stopped then this method + // has no effect. + void MaybeStartBuffering(); + // After this function is called, |this| will not do buffering. Already // buffered data still waits to be consumed, but after all the buffered data // is consumed, BeginRead and EndRead will result in BeginRead and EndRead // calls to the original BytesConsumer. - void StopBuffering() { is_buffering_ = false; } + void StopBuffering(); // BufferingBytesConsumer Result BeginRead(const char** buffer, size_t* available) override; @@ -58,14 +81,24 @@ void Trace(blink::Visitor*) override; private: + void OnTimerFired(TimerBase*); + // BufferingBytesConsumer::Client void OnStateChange() override; void BufferData(); const Member<BytesConsumer> bytes_consumer_; + TaskRunnerTimer<BufferingBytesConsumer> timer_; Deque<Vector<char>> buffer_; size_t offset_for_first_chunk_ = 0; - bool is_buffering_ = true; + + enum class BufferingState { + kDelayed, + kStarted, + kStopped, + }; + BufferingState buffering_state_ = BufferingState::kDelayed; + bool has_seen_end_of_data_ = false; bool has_seen_error_ = false; Member<BytesConsumer::Client> client_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc index 9d62ab8..09b5f26 100644 --- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc +++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
@@ -4,7 +4,12 @@ #include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/scoped_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h" #include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h" #include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h" #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h" @@ -14,9 +19,28 @@ class BufferingBytesConsumerTest : public testing::Test { public: + BufferingBytesConsumerTest() + : task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {} + using Command = ReplayingBytesConsumer::Command; using Result = BytesConsumer::Result; using PublicState = BytesConsumer::PublicState; + + protected: + mojo::ScopedDataPipeConsumerHandle MakeDataPipe() { + MojoCreateDataPipeOptions data_pipe_options{ + sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, + 0}; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + mojo::ScopedDataPipeProducerHandle producer_handle; + CHECK_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(&data_pipe_options, &producer_handle, + &consumer_handle)); + return consumer_handle; + } + + base::test::ScopedTaskEnvironment task_environment_; }; TEST_F(BufferingBytesConsumerTest, Read) { @@ -35,7 +59,7 @@ replaying_bytes_consumer->Add(Command(Command::kDone)); auto* bytes_consumer = - MakeGarbageCollected<BufferingBytesConsumer>(replaying_bytes_consumer); + BufferingBytesConsumer::Create(replaying_bytes_consumer); EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(bytes_consumer); @@ -46,6 +70,45 @@ EXPECT_EQ("12345678", String(result.second.data(), result.second.size())); } +TEST_F(BufferingBytesConsumerTest, ReadWithDelay) { + auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); + auto* replaying_bytes_consumer = + MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); + + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); + + replaying_bytes_consumer->Add(Command(Command::kWait)); + replaying_bytes_consumer->Add(Command(Command::kData, "1")); + replaying_bytes_consumer->Add(Command(Command::kWait)); + replaying_bytes_consumer->Add(Command(Command::kWait)); + replaying_bytes_consumer->Add(Command(Command::kData, "23")); + replaying_bytes_consumer->Add(Command(Command::kData, "4")); + replaying_bytes_consumer->Add(Command(Command::kData, "567")); + replaying_bytes_consumer->Add(Command(Command::kData, "8")); + replaying_bytes_consumer->Add(Command(Command::kDone)); + + auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay( + replaying_bytes_consumer, + scheduler::GetSingleThreadTaskRunnerForTesting()); + + task_runner->RunUntilIdle(); + + // The underlying consumer should not have been read yet due to the delay. + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + EXPECT_EQ(PublicState::kReadableOrWaiting, + replaying_bytes_consumer->GetPublicState()); + + auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(bytes_consumer); + auto result = reader->Run(task_runner.get()); + + // Reading before the delay expires should still work correctly. + EXPECT_EQ(PublicState::kClosed, bytes_consumer->GetPublicState()); + ASSERT_EQ(result.first, Result::kDone); + EXPECT_EQ("12345678", String(result.second.data(), result.second.size())); +} + TEST_F(BufferingBytesConsumerTest, Buffering) { auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* replaying_bytes_consumer = @@ -61,7 +124,7 @@ replaying_bytes_consumer->Add(Command(Command::kDone)); auto* bytes_consumer = - MakeGarbageCollected<BufferingBytesConsumer>(replaying_bytes_consumer); + BufferingBytesConsumer::Create(replaying_bytes_consumer); EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, @@ -80,6 +143,54 @@ EXPECT_EQ("12345678", String(result.second.data(), result.second.size())); } +TEST_F(BufferingBytesConsumerTest, BufferingWithDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); + + auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); + auto* replaying_bytes_consumer = + MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); + + replaying_bytes_consumer->Add(Command(Command::kWait)); + replaying_bytes_consumer->Add(Command(Command::kData, "1")); + replaying_bytes_consumer->Add(Command(Command::kData, "23")); + replaying_bytes_consumer->Add(Command(Command::kData, "4")); + replaying_bytes_consumer->Add(Command(Command::kWait)); + replaying_bytes_consumer->Add(Command(Command::kData, "567")); + replaying_bytes_consumer->Add(Command(Command::kData, "8")); + replaying_bytes_consumer->Add(Command(Command::kDone)); + + auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay( + replaying_bytes_consumer, + scheduler::GetSingleThreadTaskRunnerForTesting()); + + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + EXPECT_EQ(PublicState::kReadableOrWaiting, + replaying_bytes_consumer->GetPublicState()); + + task_runner->RunUntilIdle(); + + // The underlying consumer should not have been read yet due to the delay. + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + EXPECT_EQ(PublicState::kReadableOrWaiting, + replaying_bytes_consumer->GetPublicState()); + + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(11)); + task_runner->RunUntilIdle(); + + // After the delay expires the underlying consumer should be completely read. + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + EXPECT_EQ(PublicState::kClosed, replaying_bytes_consumer->GetPublicState()); + + auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(bytes_consumer); + auto result = reader->Run(task_runner.get()); + + EXPECT_EQ(PublicState::kClosed, bytes_consumer->GetPublicState()); + ASSERT_EQ(result.first, Result::kDone); + EXPECT_EQ("12345678", String(result.second.data(), result.second.size())); +} + TEST_F(BufferingBytesConsumerTest, StopBuffering) { auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* replaying_bytes_consumer = @@ -95,7 +206,7 @@ replaying_bytes_consumer->Add(Command(Command::kDone)); auto* bytes_consumer = - MakeGarbageCollected<BufferingBytesConsumer>(replaying_bytes_consumer); + BufferingBytesConsumer::Create(replaying_bytes_consumer); bytes_consumer->StopBuffering(); EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); @@ -116,5 +227,62 @@ EXPECT_EQ("12345678", String(result.second.data(), result.second.size())); } +TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeFailsWithoutDelay) { + auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); + + DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; + DataPipeBytesConsumer* data_pipe_consumer = + MakeGarbageCollected<DataPipeBytesConsumer>(task_runner, MakeDataPipe(), + ¬ifier); + + auto* bytes_consumer = BufferingBytesConsumer::Create(data_pipe_consumer); + + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + auto pipe = bytes_consumer->DrainAsDataPipe(); + EXPECT_FALSE(pipe.is_valid()); +} + +TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeSucceedsWithDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); + + auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); + + DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; + DataPipeBytesConsumer* data_pipe_consumer = + MakeGarbageCollected<DataPipeBytesConsumer>(task_runner, MakeDataPipe(), + ¬ifier); + + auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay( + data_pipe_consumer, scheduler::GetSingleThreadTaskRunnerForTesting()); + + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + auto drained_consumer_handle = bytes_consumer->DrainAsDataPipe(); + EXPECT_TRUE(drained_consumer_handle.is_valid()); +} + +TEST_F(BufferingBytesConsumerTest, DrainAsDataPipeFailsWithExpiredDelay) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeatureWithParameters( + features::kBufferingBytesConsumerDelay, {{"milliseconds", "10"}}); + + auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); + + DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; + DataPipeBytesConsumer* data_pipe_consumer = + MakeGarbageCollected<DataPipeBytesConsumer>(task_runner, MakeDataPipe(), + ¬ifier); + + auto* bytes_consumer = BufferingBytesConsumer::CreateWithDelay( + data_pipe_consumer, scheduler::GetSingleThreadTaskRunnerForTesting()); + + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(11)); + + EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState()); + auto drained_consumer_handle = bytes_consumer->DrainAsDataPipe(); + EXPECT_FALSE(drained_consumer_handle.is_valid()); +} + } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc index 4acc782..731f6a76 100644 --- a/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc +++ b/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -268,8 +268,8 @@ if (!client && GetResourceRequest().UseStreamOnResponse()) { // For preload, we want to store the body while dispatching // onload and onerror events. - bytes_consumer_for_preload_ = MakeGarbageCollected<BufferingBytesConsumer>( - &body_loader.DrainAsBytesConsumer()); + bytes_consumer_for_preload_ = + BufferingBytesConsumer::Create(&body_loader.DrainAsBytesConsumer()); return; }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index cd7c265..e53e5a69 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1267,9 +1267,6 @@ status: "experimental", }, { - name: "RedirectBlankNavigationToTop", - }, - { name: "ReducedReferrerGranularity", }, {
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint index e2ea9f7a..811a9445 100644 --- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint +++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=CompositeAfterPaint
@@ -21,10 +21,14 @@ # Fail before CompositeAfterPaint but pass with it. crbug.com/472330 fast/borders/border-image-outset-split-inline-vertical-lr.html [ Pass ] -crbug.com/817175 compositing/overflow/clip-escaping-reverse-order-should-not-crash.html [ Pass ] crbug.com/736052 compositing/overflow/composited-scroll-with-fractional-translation.html [ Pass ] -crbug.com/857501 compositing/gestures/gesture-tapHighlight-with-filter.html [ Pass ] crbug.com/802915 css3/blending/isolation-should-include-non-local-background.html [ Pass ] +crbug.com/363609 external/wpt/css/css-transforms/rotateY-180deg-with-overflow-scroll.html [ Pass ] +crbug.com/918155 scrollbars/overlay-scrollbar-over-child-layer.html [ Pass ] +crbug.com/887000 virtual/prefer_compositing_to_lcd_text/scrollbars/hidden-scrollbars-invisible.html [ Pass ] +crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer.html [ Pass ] +crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested-2.html [ Pass ] +crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer-nested.html [ Pass ] Bug(none) virtual/android/fullscreen/video-overlay-scroll.html [ Failure ] Bug(none) virtual/android/rootscroller/nested-rootscroller-browser-controls-bounds-hidden.html [ Crash ] @@ -74,7 +78,6 @@ Bug(none) compositing/overflow/overflow-scroll-with-transparent-background.html [ Failure ] Bug(none) compositing/overflow/resize-painting.html [ Failure ] Bug(none) compositing/overflow/scaled-overflow.html [ Failure ] -Bug(none) compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ] crbug.com/667946 compositing/overflow/scrolls-with-respect-to-nested.html [ Failure ] crbug.com/667946 compositing/overflow/scrolls-with-respect-to-transform.html [ Failure ] crbug.com/667946 compositing/overflow/scrolls-with-respect-to.html [ Failure ] @@ -115,6 +118,7 @@ Bug(none) fast/sub-pixel/transformed-iframe-copy-on-scroll.html [ Failure ] Bug(none) fragmentation/outline-crossing-columns.html [ Crash ] Bug(none) fullscreen/compositor-touch-hit-rects-fullscreen-video-controls.html [ Failure ] +Bug(none) http/tests/input/discard-events-to-unstable-iframe.html [ Failure ] Bug(none) paint/pagination/pagination-change-clip-crash.html [ Failure ] Bug(none) printing/fixed-positioned-headers-and-footers-absolute-covering-some-pages.html [ Failure ] Bug(none) printing/fixed-positioned-headers-and-footers-larger-than-page.html [ Failure ] @@ -211,9 +215,6 @@ # Subpixel or 1-pixel differences. May be real subpixel handling bugs. Bug(none) fast/forms/text/input-appearance-autocomplete-very-long-value.html [ Failure ] -Bug(none) fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ] -Bug(none) virtual/threaded/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Skip ] -Bug(none) virtual/scroll_customization/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Skip ] Bug(none) svg/custom/grayscale-gradient-mask-2.svg [ Failure ] Bug(none) paint/invalidation/scroll/scroll-in-transformed-layer.html [ Failure ] Bug(none) paint/invalidation/scroll/scroll-with-transformed-parent-layer.html [ Failure ] @@ -441,9 +442,6 @@ crbug.com/940033 virtual/fractional_scrolling_threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ] crbug.com/940033 virtual/threaded/fast/scrolling/wheel-scrolling-over-custom-scrollbar.html [ Failure ] -crbug.com/918155 scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ] -crbug.com/918155 virtual/prefer_compositing_to_lcd_text/scrollbars/overlay-scrollbar-over-child-layer.html [ Failure ] - crbug.com/979389 ietestcenter/css3/bordersbackgrounds/background-attachment-local-scrolling.htm [ Failure ] # Crash during PictureLayer::GetPicture() when DisplayItemList is finished twice.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index e27a5236..099634c 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -143,10 +143,6 @@ crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ] crbug.com/771643 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ] -# Fails due to flaws in the SPv1 architecture. Can be fixed with composite-after-paint. -crbug.com/862483 compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ] -crbug.com/862483 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ] - crbug.com/807395 fast/multicol/mixed-opacity-test.html [ Failure ] ########## Ref tests can't be rebaselined ########## @@ -240,9 +236,7 @@ crbug.com/907601 external/wpt/dom/events/scrolling/scrollend-event-fired-after-snap.html [ Skip ] # Tests not yet passing with Blink FractionalScrollOffsets enabled. -crbug.com/414283 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ] crbug.com/414283 virtual/fractional_scrolling/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Failure Pass ] -crbug.com/414283 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ] crbug.com/414283 virtual/fractional_scrolling_threaded/fast/scrolling/fractional-scroll-offset-iframe-fixed-position.html [ Failure Pass ] crbug.com/980969 [ Mac ] http/tests/input/discard-events-to-unstable-iframe.html [ Pass Failure ] @@ -1043,7 +1037,7 @@ crbug.com/591099 [ Mac10.13 ] fast/dynamic/insert-before-table-part-in-continuation.html [ Failure ] crbug.com/591099 [ Mac10.13 ] fast/dynamic/noninlinebadness.html [ Failure ] crbug.com/591099 [ Mac10.13 ] fast/dynamic/outerHTML-doc.html [ Failure ] -crbug.com/591099 [ Mac ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ] +#crbug.com/591099 [ Mac ] fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ] crbug.com/591099 [ Mac10.13 ] fast/forms/basic-inputs.html [ Failure ] crbug.com/591099 [ Mac10.13 ] fast/forms/button/button-inner-block-reuse.html [ Failure ] crbug.com/591099 [ Mac10.13 ] fast/forms/formmove2.html [ Failure ] @@ -1331,10 +1325,15 @@ crbug.com/591099 [ Mac10.13 ] virtual/gpu-rasterization/images/imagemap-focus-ring-outline-color.html [ Failure ] crbug.com/591099 [ Mac10.13 ] virtual/gpu-rasterization/images/imagemap-focus-ring-zoom.html [ Failure ] crbug.com/591099 [ Mac10.13 ] virtual/gpu-rasterization/images/imagemap-overflowing-circle-focus-ring.html [ Failure ] -crbug.com/591099 [ Mac ] virtual/mouseevent_fractional/fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ] +#crbug.com/591099 [ Mac ] virtual/mouseevent_fractional/fast/events/before-unload-return-value-from-listener.html [ Crash Timeout ] crbug.com/591099 [ Mac10.13 ] virtual/scroll_customization/fast/events/touch/compositor-touch-hit-rects-continuation.html [ Failure ] -crbug.com/974725 [ Win7 ] fast/events/before-unload-return-value-from-listener.html [ Pass Crash ] +#crbug.com/974725 [ Win7 ] fast/events/before-unload-return-value-from-listener.html [ Pass Crash ] + +# A few other lines for this test are commented out above to avoid conflicting expectations. +# If they are not also fixed, reinstate them when removing these lines. +crbug.com/982211 fast/events/before-unload-return-value-from-listener.html [ Pass Crash Timeout ] +crbug.com/982211 virtual/mouseevent_fractional/fast/events/before-unload-return-value-from-listener.html [ Pass Crash Timeout ] # Maybe a Mac-specific rebaselining issue. crbug.com/846557 [ Mac ] virtual/layout_ng_experimental/css3/flexbox/button.html [ Skip ] @@ -2640,7 +2639,6 @@ crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ] crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ] crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ] -crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ] crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ] crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ] crbug.com/613672 [ Mac ] fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ] @@ -2658,7 +2656,6 @@ crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ] crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ] crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ] -crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ] crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ] crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ] crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ] @@ -2676,7 +2673,6 @@ crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-noscroll-body-xhidden.html [ Skip ] crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-noscroll-body-yhidden.html [ Skip ] crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-past-extent-diagonally.html [ Skip ] -crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html [ Skip ] crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div-zoomed.html [ Skip ] crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-div.html [ Skip ] crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/gesture/touch-gesture-scroll-iframe-editable.html [ Skip ] @@ -3216,12 +3212,6 @@ # ====== New tests from wpt-importer added here ====== crbug.com/626703 external/wpt/webauthn/idlharness-manual.https.window.js [ Skip ] -crbug.com/626703 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-003.html [ Failure ] -crbug.com/626703 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-004.html [ Failure ] -crbug.com/626703 virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-005.html [ Failure ] -crbug.com/626703 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-003.html [ Failure ] -crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-005.html [ Failure ] -crbug.com/626703 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-004.html [ Failure ] crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-fraktur-001.tentative.html [ Failure ] crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-stretched-001.tentative.html [ Failure ] crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-looped-001.tentative.html [ Failure ] @@ -3390,10 +3380,6 @@ crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/request/request-headers.html [ Timeout ] crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer/meta-referrer/cross-origin/http-https/iframe-tag/no-redirect/generic.http.html [ Timeout ] crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/fetch/api/request/request-bad-port.html [ Timeout ] -crbug.com/626703 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-001.html [ Failure ] -crbug.com/626703 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-002.html [ Failure ] -crbug.com/626703 [ Mac ] virtual/layout_ng_experimental/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-002.html [ Failure ] -crbug.com/626703 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-select-elem-001.html [ Failure ] crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/font-family-name-025.html [ Failure ] crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer/http-rp/same-origin/http-http/script-tag/keep-origin-redirect/generic.http.html [ Timeout ] crbug.com/626703 [ Retina ] virtual/blink-cors/external/wpt/referrer-policy/no-referrer-when-downgrade/http-rp/same-origin/http-http/script-tag/swap-origin-redirect/insecure-protocol.http.html [ Timeout ] @@ -4550,6 +4536,8 @@ crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/geometry-background-image-tiled-001.https.html [ Crash ] crbug.com/957457 virtual/threaded/external/wpt/css/css-paint-api/invalid-image-pending-script.https.html [ Crash ] +crbug.com/982116 http/tests/devtools/elements/styles-4/styles-new-API.js [ Pass Timeout ] + # Shared memory growth temporarily disabled. crbug.com/951795 external/wpt/wasm/jsapi/memory/grow.any.html [ Pass Failure ] crbug.com/951795 external/wpt/wasm/jsapi/memory/grow.any.worker.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png index 86f7a203..d3100be 100644 --- a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png +++ b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt index c856c62..d6d5879 100644 --- a/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt +++ b/third_party/blink/web_tests/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
@@ -18,26 +18,24 @@ "backgroundColor": "#FFFFFF" }, { - "name": "LayoutBlockFlow (positioned) DIV class='outer'", - "position": [6, 6], - "bounds": [340, 282] + "name": "LayoutNGBlockFlow (positioned) DIV class='outer'", + "bounds": [352, 294] }, { - "name": "LayoutBlockFlow (relative positioned) DIV class='content'", + "name": "LayoutNGBlockFlow (relative positioned) DIV class='content'", "position": [79, 79], "bounds": [196, 212], "contentsOpaque": true, "backgroundColor": "#DDDDDD" }, { - "name": "LayoutBlockFlow (positioned) DIV class='outer' (foreground) Layer", - "position": [6, 6], - "bounds": [340, 282] + "name": "LayoutNGBlockFlow (positioned) DIV class='outer' (foreground) Layer", + "bounds": [352, 294] }, { - "name": "LayoutBlockFlow DIV class='scroller'", - "position": [38, 38], - "bounds": [278, 218], + "name": "LayoutNGBlockFlow DIV class='scroller'", + "position": [32, 32], + "bounds": [290, 230], "backgroundColor": "#FFFFFF" }, {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-logical.html b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-logical.html new file mode 100644 index 0000000..a06132fe --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-logical.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test flow-relative versions of overscroll-behavior properties</title> +<link rel="author" title="Majid Valipour"> +<link rel="help" href="https://drafts.csswg.org/css-overscroll-behavior-1/#overscroll-behavior-longhands-logical"> +<link rel="help" href="https://drafts.csswg.org/css-logical/#box"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +div { + overscroll-behavior-block: contain; + overscroll-behavior-inline: none; +} +#horizontal { + writing-mode: horizontal-tb; +} +#vertical { + writing-mode: vertical-rl; +} +#horizontalreversed { + writing-mode: horizontal-tb; + direction: rtl; +} +</style> +<body> + <div id="horizontal"></div> + <div id="vertical"></div> + <div id="horizontalreversed"></div> +</body> + +<script> +test(function() { + var element = document.getElementById("horizontal"); + assert_equals(getComputedStyle(element).overscrollBehaviorX, "none"); + assert_equals(getComputedStyle(element).overscrollBehaviorY, "contain"); +}, "Logical overscroll-behavior maps correctly when element has horizontal-tb writing mode"); + +test(function() { + var element = document.getElementById("vertical"); + assert_equals(getComputedStyle(element).overscrollBehaviorX, "contain"); + assert_equals(getComputedStyle(element).overscrollBehaviorY, "none"); +}, "Logical overscroll-behavior maps correctly when element has vertical-rl writing mode"); + +test(function() { + var element = document.getElementById("horizontalreversed"); + assert_equals(getComputedStyle(element).overscrollBehaviorX, "none"); + assert_equals(getComputedStyle(element).overscrollBehaviorY, "contain"); +}, "Logical overscroll-behavior maps correctly when element has horizontal-tb writing mode and is not affected by rtl direction"); +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-computed.html b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-computed.html index af117f1..b587ec5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-computed.html
@@ -6,6 +6,8 @@ <link rel="help" href="https://drafts.csswg.org/css-overscroll-behavior/#overscroll-behavior-properties"> <meta name="assert" content="overscroll-behavior-x computed value is as specified."> <meta name="assert" content="overscroll-behavior-y computed value is as specified."> +<meta name="assert" content="overscroll-behavior-inline computed value is as specified."> +<meta name="assert" content="overscroll-behavior-block computed value is as specified."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/computed-testcommon.js"></script> @@ -14,7 +16,7 @@ <div id="target"></div> <script> 'use strict'; -for (let property of ["overscroll-behavior-x", "overscroll-behavior-y"]) { +for (let property of ["overscroll-behavior-x", "overscroll-behavior-y", "overscroll-behavior-inline", "overscroll-behavior-block"]) { test_computed_value(property, "contain"); test_computed_value(property, "none"); test_computed_value(property, "auto");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-invalid.html index a29722f..02ada457 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-invalid.html
@@ -18,7 +18,7 @@ test_invalid_value("overscroll-behavior", "contain contain contain"); -for (let property of ["overscroll-behavior-x", "overscroll-behavior-y"]) { +for (let property of ["overscroll-behavior-x", "overscroll-behavior-y", "overscroll-behavior-inline", "overscroll-behavior-block"]) { test_invalid_value(property, "normal"); test_invalid_value(property, "0"); test_invalid_value(property, "contain contain");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-valid.html b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-valid.html index 9dbd4fb..45d90d4 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/parsing/overscroll-behavior-valid.html
@@ -26,7 +26,7 @@ test_valid_value("overscroll-behavior", "auto auto", "auto"); -for (let property of ["overscroll-behavior-x", "overscroll-behavior-y"]) { +for (let property of ["overscroll-behavior-x", "overscroll-behavior-y", "overscroll-behavior-inline", "overscroll-behavior-block"]) { test_valid_value(property, "contain"); test_valid_value(property, "none"); test_valid_value(property, "auto");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html index 0ab50d6..5ef2009 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html +++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/snap-at-user-scroll-end-manual.html
@@ -6,7 +6,6 @@ <style> body { margin: 0px; - overflow: scroll; scroll-snap-type: both mandatory; } #content { @@ -22,6 +21,7 @@ width: 400px; height: 400px; background-color: lightblue; + overflow: hidden; scroll-snap-align: start; } #i1 { @@ -74,10 +74,13 @@ if (!scrolled_x || !scrolled_y) return; - assert_equals(window.scrollX, target.offsetLeft, - "window.scrollX should be at snapped position."); - assert_equals(window.scrollY, target.offsetTop, - "window.scrollY should be at snapped position."); + snap_test.step(() => { + assert_equals(window.scrollX, target.offsetLeft, + "window.scrollX should be at snapped position."); + assert_equals(window.scrollY, target.offsetTop, + "window.scrollY should be at snapped position."); + }); + // To make the test result visible. var content = document.getElementById("content"); body.removeChild(content);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-006.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-006.html new file mode 100644 index 0000000..91439a6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-006.html
@@ -0,0 +1,36 @@ +<!doctype html> +<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org"> +<link rel="help" href="https://www.w3.org/TR/css-position-3/#def-cb"> +<link rel="match" href="../reference/ref-filled-green-100px-square-only.html"> +<link rel="bookmark" href="https://crbug.com/977507" /> +<meta name="flags" content="" /> +<meta name="assert" content="Abspos table works when it is dynamically added" /> + +<style> +table { + border-spacing: 0px; +} +td { + padding: 0px; +} +.outerTable { + height: 100px; + width: 100px; + position: relative; +} +.innerTable { + position: absolute; + top: 0px; + width: 100px; + height: 100%; + color: green; + background: green; +} +</style> +<p>Test passes if there is a filled green square.</p> +<table class=outerTable> + <td id=outerCell></td> +</table> +<script> +outerCell.innerHTML = "<table class=innerTable><td>some text</td></table>"; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html deleted file mode 100644 index 3af893a..0000000 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html +++ /dev/null
@@ -1,54 +0,0 @@ -<!DOCTYPE HTML> -<meta charset=utf-8> -<title>Element Timing: observe elements from same-origin iframes</title> -<body> -<style> -body { - margin: 0; -} -</style> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/element-timing-helpers.js"></script> -<script> - async_test((t) => { - if (!window.PerformanceElementTiming) { - assert_unreached("PerformanceElementTiming is not implemented"); - } - let beforeRender; - let img; - const img_src = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/' - + 'resources/TAOImage.py?tao=wildcard'; - const observer = new PerformanceObserver( - t.step_func_done((entryList) => { - assert_equals(entryList.getEntries().length, 1); - const entry = entryList.getEntries()[0]; - checkElement(entry, img_src, 'my_image', 'my_id', beforeRender, img); - // Assume viewport has size at least 20, so the element is fully visible. - checkRect(entry, [0, 20, 0, 20]); - checkNaturalSize(entry, 20, 20); - }) - ); - observer.observe({entryTypes: ['element']}); - // We add the image during onload to be sure that the observer is registered - // in time for it to observe the element timing. - // TODO(npm): change observer to use buffered flag. - window.onload = t.step_func(() => { - img = document.createElement('img'); - img.src = img_src; - img.setAttribute('elementtiming', 'my_image'); - img.setAttribute('id', 'my_id'); - img.onload = t.step_func(() => { - // After a short delay, assume that the entry was not dispatched. - t.step_timeout(() => { - assert_unreached("Should have received an entry!"); - t.done(); - }, 100); - }); - document.body.appendChild(img); - beforeRender = performance.now(); - }); - }, 'Cross-origin element with wildcard TAO is observed.'); -</script> - -</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO.sub.html new file mode 100644 index 0000000..83a7a5ca --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO.sub.html
@@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Element Timing: observe cross origin images with various Timing-Allow-Origin headers</title> +<body> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/element-timing-helpers.js"></script> +<script> + async_test(t => { + if (!window.PerformanceElementTiming) { + assert_unreached("PerformanceElementTiming is not implemented"); + } + const beforeRender = performance.now(); + const remote_img = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/resources/TAOImage.py?' + + 'origin=' + window.location.origin +'&tao='; + const valid_tao = ['wildcard', 'origin', 'multi', 'multi_wildcard', 'match_origin', 'match_wildcard']; + function addImage(tao) { + const img = document.createElement('img'); + img.src = remote_img + tao; + img.setAttribute('elementtiming', tao); + img.id = 'id_' + tao; + document.body.appendChild(img); + } + valid_tao.forEach(tao => { + addImage(tao); + }); + const invalid_tao = ['null', 'space', 'uppercase']; + invalid_tao.forEach(tao => { + addImage(tao); + }); + let img_count = 0; + const total_images = valid_tao.length + invalid_tao.length; + new PerformanceObserver( + t.step_func(entryList => { + entryList.getEntries().forEach(entry => { + img_count++; + const tao = entry.identifier; + const img = document.getElementById('id_' + tao); + if (valid_tao.includes(tao)) { + checkElement(entry, remote_img + tao, tao, 'id_' + tao, beforeRender, img); + } else if (invalid_tao.includes(tao)) { + assert_equals(entry.startTime, 0, 'Entry with tao=' + tao + ' must have a startTime of 0'); + checkElement(entry, remote_img + tao, tao, 'id_' + tao, 0, img); + } + else { + assert_unreached('Should be in one of valid_tao OR invalid_tao'); + } + checkNaturalSize(entry, 20, 20); + if (img_count == total_images) + t.done(); + }); + }) + ).observe({type: 'element', buffered: true}); + }, 'Cross-origin elements with valid TAO have correct startTime, with invalid TAO have startTime set to 0.'); +</script> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/TAOImage.py b/third_party/blink/web_tests/external/wpt/element-timing/resources/TAOImage.py index 5d042c4..1e0afb3 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/resources/TAOImage.py +++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/TAOImage.py
@@ -1,7 +1,7 @@ import os def main(request, response): - origin = request.GET.first('origin', ''); + origin = request.GET.first('origin') if origin: response.headers.set('Access-Control-Allow-Origin', origin)
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/TAO-case-insensitive-null-opaque-origin.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/TAO-case-insensitive-null-opaque-origin.sub.html index 5b222cdf..5d58f1c5 100644 --- a/third_party/blink/web_tests/external/wpt/resource-timing/TAO-case-insensitive-null-opaque-origin.sub.html +++ b/third_party/blink/web_tests/external/wpt/resource-timing/TAO-case-insensitive-null-opaque-origin.sub.html
@@ -34,7 +34,7 @@ "};" + "let observer = new PerformanceObserver(observe);" + "observer.observe({ entryTypes: ['resource'] });" + - "fetch(url);" + + "fetch(url).then(r => r.text());" + "</" + "script></body>"; document.getElementById("frameContext").src = frame_content; </script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/TAO-null-opaque-origin.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/TAO-null-opaque-origin.sub.html index c78e590..0a45763 100644 --- a/third_party/blink/web_tests/external/wpt/resource-timing/TAO-null-opaque-origin.sub.html +++ b/third_party/blink/web_tests/external/wpt/resource-timing/TAO-null-opaque-origin.sub.html
@@ -34,7 +34,7 @@ "};" + "let observer = new PerformanceObserver(observe);" + "observer.observe({ entryTypes: ['resource'] });" + - "fetch(url);" + + "fetch(url).then(r => r.text());" + "</" + "script></body>"; document.getElementById("frameContext").src = frame_content; </script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/buffer-full-add-after-full-event.html b/third_party/blink/web_tests/external/wpt/resource-timing/buffer-full-add-after-full-event.html index 9694465..84d257e 100644 --- a/third_party/blink/web_tests/external/wpt/resource-timing/buffer-full-add-after-full-event.html +++ b/third_party/blink/web_tests/external/wpt/resource-timing/buffer-full-add-after-full-event.html
@@ -12,7 +12,7 @@ <script> let eventFired = false; let loadRandomResource = () => { - return fetch(window.location.href + "?" + Math.random()); + return fetch(window.location.href + "?" + Math.random()).then(r => r.text()); } setup(() => {
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-TAO-crossorigin-port.sub.html b/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-TAO-crossorigin-port.sub.html index 39f5d13..97d77fcc 100644 --- a/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-TAO-crossorigin-port.sub.html +++ b/third_party/blink/web_tests/external/wpt/resource-timing/resources/iframe-TAO-crossorigin-port.sub.html
@@ -23,7 +23,7 @@ } let observer = new PerformanceObserver(observe); observer.observe({ entryTypes: ["resource"] }); - fetch(url); + fetch(url).then(r => r.text()); </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js index e584ea3..261e1cf 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/webxr-test.js
@@ -3,6 +3,19 @@ // This polyfill library implements the WebXR Test API as specified here: // https://github.com/immersive-web/webxr-test-api + +let default_standing = new gfx.mojom.Transform(); +default_standing.matrix = [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 1.65, 0, 1]; +const default_stage_parameters = { + standingTransform: default_standing, + sizeX: 0, + sizeZ: 0, + bounds: null +}; + class ChromeXRTest { constructor() { this.mockVRService_ = new MockVRService(mojo.frameInterfaces); @@ -147,6 +160,8 @@ this.pose_ = null; this.next_frame_id_ = 0; + this.bounds_ = null; + this.send_pose_reset_ = false; this.service_ = service; @@ -168,6 +183,13 @@ this.setViewerOrigin(fakeDeviceInit.viewerOrigin); } + if (fakeDeviceInit.localToFloorLevelTransform != null) { + this.setLocalToFloorLevelTransform(fakeDeviceInit.localToFloorLevelTransform); + } + + // This appropriately handles if the coordinates are null + this.setBoundsGeometry(fakeDeviceInit.boundsCoordinates); + this.setViews(fakeDeviceInit.views); } @@ -210,7 +232,91 @@ this.pose_ = null; } + simulateVisibilityChange(visibilityState) { + // TODO(https://crbug.com/982099): Chrome currently does not have a way for + // devices to bubble up any form of visibilityChange. + } + + setBoundsGeometry(bounds) { + if (bounds == null) { + this.bounds_ = null; + } else if (bounds.length < 3) { + throw new Error("Bounds must have a length of at least 3"); + } else { + this.bounds_ = bounds; + } + + // We can only set bounds if we have stageParameters set; otherwise, we + // don't know the transform from local space to bounds space. + // We'll cache the bounds so that they can be set in the future if the + // floorLevel transform is set, but we won't update them just yet. + if (this.displayInfo_.stageParameters) { + this.displayInfo_.stageParameters.bounds = this.bounds_; + + if (this.sessionClient_.ptr.isBound()) { + this.sessionClient_.onChanged(this.displayInfo_); + } + } + } + + setLocalToFloorLevelTransform(transform) { + if (!this.displayInfo_.stageParameters) { + this.displayInfo_.stageParameters = default_stage_parameters; + this.displayInfo_.stageParameters.bounds = this.bounds_; + } + + this.displayInfo_.stageParameters.standingTransform = new gfx.mojom.Transform(); + this.displayInfo_.stageParameters.standingTransform.matrix = + this.getMatrixFromTransform(transform); + + if (this.sessionClient_.ptr.isBound()) { + this.sessionClient_.onChanged(this.displayInfo_); + } + } + + clearLocalToFloorLevelTransform() { + if (this.displayInfo_.stageParameters) { + this.displayInfo_.stageParameters = null; + + if (this.sessionClient_.ptr.isBound()) { + this.sessionClient_.onChanged(this.displayInfo_); + } + } + } + + simulateResetPose() { + this.send_pose_reset_ = true; + } + // Helper methods + getMatrixFromTransform(transform) { + let x = transform.orientation[0]; + let y = transform.orientation[1]; + let z = transform.orientation[2]; + let w = transform.orientation[3]; + + let m11 = 1.0 - 2.0 * (y * y + z * z); + let m21 = 2.0 * (x * y + z * w); + let m31 = 2.0 * (x * z - y * w); + + let m12 = 2.0 * (x * y - z * w); + let m22 = 1.0 - 2.0 * (x * x + z * z); + let m32 = 2.0 * (y * z + x * w); + + let m13 = 2.0 * (x * z + y * w); + let m23 = 2.0 * (y * z - x * w); + let m33 = 1.0 - 2.0 * (x * x + y * y); + + let m14 = transform.position[0]; + let m24 = transform.position[1]; + let m34 = transform.position[2]; + + // Column-major linearized order is expected. + return [m11, m21, m31, 0, + m12, m22, m32, 0, + m13, m23, m33, 0, + m14, m24, m34, 1]; + } getNonImmersiveDisplayInfo() { let displayInfo = this.getImmersiveDisplayInfo(); @@ -297,6 +403,8 @@ getFrameData() { if (this.pose_) { this.pose_.poseIndex++; + this.pose_.poseReset = this.send_pose_reset_; + this.send_pose_reset_ = false; } // Convert current document time to monotonic time.
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/actions.html b/third_party/blink/web_tests/external/wpt/std-toast/actions.html new file mode 100644 index 0000000..be453d5 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/std-toast/actions.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Toast: action tests</title> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<main></main> + +<script type="module"> +import { testActionToast, testToastElement } from './resources/helpers.js'; + +testActionToast((toast) => { + assert_equals(toast.action.textContent, 'action'); +}, 'the action element gets properly captured with this.action'); + +testActionToast((toast) => { + toast.innerHTML = `<button slot='action'>new action</button>` + assert_equals(toast.action.textContent, 'new action'); +}, 'changing the action button changes this.action'); + +testToastElement((toast) => { + assert_equals(toast.action, null); +}, 'the action property of a toast without an action is null'); + +testToastElement((toast) => { + toast.innerHTML = `<button slot="action" id="first">first</button> + <button slot="action" id="second">second</button>`; + + assert_equals(toast.action, toast.querySelector('#first')); +}) +</script>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html index 2aef41ae..58a515d 100644 --- a/third_party/blink/web_tests/external/wpt/std-toast/attributes.html +++ b/third_party/blink/web_tests/external/wpt/std-toast/attributes.html
@@ -115,7 +115,7 @@ }, 'toggling open attribute does not start timeout'); testToastElement((toast) => { - const permitted_properties = ['constructor', 'show', 'hide', 'toggle', 'open']; + const permitted_properties = ['constructor', 'show', 'hide', 'toggle', 'open', 'action']; assert_array_equals(permitted_properties.sort(), Object.getOwnPropertyNames(toast.__proto__).sort()); }, 'toast only exposes certain properties'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/events-open.html b/third_party/blink/web_tests/external/wpt/std-toast/events-open.html index 5442b36d..a49414d 100644 --- a/third_party/blink/web_tests/external/wpt/std-toast/events-open.html +++ b/third_party/blink/web_tests/external/wpt/std-toast/events-open.html
@@ -1,6 +1,6 @@ <!DOCTYPE html> <meta charset="utf-8"> -<title>Toast: Event tests</title> +<title>Toast: event (open) tests</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -13,9 +13,7 @@ testToastElement((toast) => { const counter = new EventCollector(); - toast.addEventListener('show', (e) => { - counter.getCallback()(e); - }); + toast.addEventListener('show', counter.getCallback()); toast.open = true; assert_equals(counter.getCount(), 1);
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/events-showhide.html b/third_party/blink/web_tests/external/wpt/std-toast/events-showhide.html index 73f1df4..547f742 100644 --- a/third_party/blink/web_tests/external/wpt/std-toast/events-showhide.html +++ b/third_party/blink/web_tests/external/wpt/std-toast/events-showhide.html
@@ -1,6 +1,6 @@ <!DOCTYPE html> <meta charset="utf-8"> -<title>Toast: Event tests</title> +<title>Toast: event (show/hide) tests</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/std-toast/options.html b/third_party/blink/web_tests/external/wpt/std-toast/options.html index 9cea971..6d4a462 100644 --- a/third_party/blink/web_tests/external/wpt/std-toast/options.html +++ b/third_party/blink/web_tests/external/wpt/std-toast/options.html
@@ -1,7 +1,7 @@ <!DOCTYPE html> <meta charset="utf-8"> <meta name="timeout" content="long"> -<title>Toast: showToast tests</title> +<title>Toast: option tests</title> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-expected.txt index 1ca662a..476c1964 100644 --- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-expected.txt +++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-addIceCandidate-expected.txt
@@ -5,8 +5,8 @@ FAIL addIceCandidate(null) should work, and add a=end-of-candidates to both m-sections promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex" FAIL addIceCandidate({}) should work, and add a=end-of-candidates to both m-sections promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex" FAIL addIceCandidate({}) in stable should work, and add a=end-of-candidates to both m-sections promise_test: Unhandled rejection with value: object "TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex" -FAIL addIceCandidate({usernameFragment: usernameFragment1, sdpMid: sdpMid1}) should work, and add a=end-of-candidates to the first m-section promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate" -FAIL addIceCandidate({usernameFragment: usernameFragment2, sdpMLineIndex: 1}) should work, and add a=end-of-candidates to the first m-section promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate" +FAIL addIceCandidate({usernameFragment: usernameFragment1, sdpMid: sdpMid1}) should work, and add a=end-of-candidates to the first m-section assert_true: Expect candidate line to be found between media lines m=audio and m=video expected true got false +FAIL addIceCandidate({usernameFragment: usernameFragment2, sdpMLineIndex: 1}) should work, and add a=end-of-candidates to the first m-section assert_true: expected true got false FAIL addIceCandidate({usernameFragment: "no such ufrag"}) should not work assert_throws: function "function() { throw e }" threw object "TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex" that is not a DOMException OperationError: property "code" is equal to undefined, expected 0 PASS Add ICE candidate after setting remote description should succeed PASS Add ICE candidate with RTCIceCandidate should succeed @@ -17,7 +17,7 @@ PASS addIceCandidate with second sdpMid and sdpMLineIndex should add candidate to second media stream PASS Add candidate for first media stream with null usernameFragment should add candidate to first media stream PASS Adding multiple candidates should add candidates to their corresponding media stream -FAIL Add with empty candidate string (end of candidate) should succeed promise_test: Unhandled rejection with value: object "OperationError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate" +FAIL Add with empty candidate string (end of candidate) should succeed assert_true: Expect candidate line to be found between media lines m=audio and m=video expected true got false PASS Add candidate with both sdpMid and sdpMLineIndex manually set to null should reject with TypeError PASS Add candidate with only valid candidate string should reject with TypeError PASS Add candidate with invalid candidate string and both sdpMid and sdpMLineIndex null should reject with TypeError
diff --git a/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html new file mode 100644 index 0000000..f15ffc5 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webxr/events_referenceSpace_reset.https.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="resources/webxr_util.js"></script> +<script src="resources/webxr_test_constants.js"></script> +<canvas id="webgl-canvas"></canvas> + +<script> +let immersiveTestName = "XRSession resetpose from a device properly fires off " + + "the right events for immersive sessions"; +let nonImmersiveTestName = "XRSession resetpose from a device properly fires off " + + "the right events for non-immersive sessions"; + +let watcherDone = new Event("watcherdone"); + +let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; + +let testFunction = function(session, fakeDeviceController, t) { + let resetPromise = session.requestReferenceSpace('local') + .then((refSpace) => { + let eventWatcher = new EventWatcher( + t, refSpace, ["reset", "watcherdone"]); + refSpace.addEventListener("reset", (event) => { + assert_equals(event.referenceSpace, refSpace); + refSpace.dispatchEvent(watcherDone); + }, false); + return eventWatcher.wait_for(["reset", "watcherdone"]); + }); + + fakeDeviceController.simulateResetPose(); + + // The triggered resetPose event should arrive after the next Animation Frame + session.requestAnimationFrame(() => {}); + + return resetPromise; +}; + +xr_session_promise_test( + immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr'); +xr_session_promise_test( + nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline'); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_constants.js b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_constants.js index a846f1d..aed6ff9c 100644 --- a/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_constants.js +++ b/third_party/blink/web_tests/external/wpt/webxr/resources/webxr_test_constants.js
@@ -37,16 +37,25 @@ const VALID_GRIP_WITH_POINTER_OFFSET = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4, 3, 3, 1]; -const VALID_STAGE_TRANSFORM = - [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.0, 1.65, -1.0, 1]; +// A Valid Local to floor matrix/transform for when we don't care about specific +// values. Note that these should be identical, just different representations. +const VALID_LOCAL_TO_FLOOR_MATRIX = [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 1.65, -1, 1]; + +const VALID_LOCAL_TO_FLOOR_TRANSFORM = { + position: [1.0, 1.65, -1.0], + orientation: [0, 0, 0, 1] +}; const VALID_BOUNDS = [ - { x: 3.0, y: 0, z: -2.0 }, - { x: 3.5, y: 0, z: 0.0 }, - { x: 3.0, y: 0, z: 2.0 }, - { x: -3.0, y: 0, z: 2.0 }, - { x: -3.5, y: 0, z: 0.0 }, - { x: -3.0, y: 0, z: -2.0 } + { x: 3.0, z: -2.0 }, + { x: 3.5, z: 0.0 }, + { x: 3.0, z: 2.0 }, + { x: -3.0, z: 2.0 }, + { x: -3.5, z: 0.0 }, + { x: -3.0, z: -2.0 } ]; const VALID_RESOLUTION = {
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrBoundedReferenceSpace_updates.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrBoundedReferenceSpace_updates.https.html new file mode 100644 index 0000000..cf1047c8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webxr/xrBoundedReferenceSpace_updates.https.html
@@ -0,0 +1,63 @@ +<!DOCTYPE html> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="resources/webxr_util.js"></script> +<script src="resources/webxr_test_constants.js"></script> +<script src="resources/xr-test-asserts.js"></script> +<canvas></canvas> + +<script> +let testName = + "'XRBoundedReferenceSpace updates properly when the changes are applied"; + +let fakeDeviceInitParams = { + supportsImmersive: true, + views: VALID_VIEWS, + viewerOrigin: IDENTITY_TRANSFORM, + localToFloorLevelTransform: VALID_LOCAL_TO_FLOOR_TRANSFORM +}; + +let testFunction = function(session, fakeDeviceController, t) { + + return new Promise((resolve, reject) => { + session.requestReferenceSpace('bounded-floor') + .then((referenceSpace) => { + t.step(() => { + assert_unreached("Should not be able to get a bounded space until bounds set"); + }); + }).catch((err) => { + t.step(() => { + assert_equals(err.name, "NotSupportedError"); + }); + + function onFrame(time, xrFrame) { + // After setting the bounds explicitly, we should be able to get a + // reference space + session.requestReferenceSpace('bounded-floor') + .then((referenceSpace) => { + t.step(() => { + assert_equals(referenceSpace.boundsGeometry.length, VALID_BOUNDS.length); + for (i = 0; i < VALID_BOUNDS.length; ++i) { + let valid_point = VALID_BOUNDS[i]; + let bounds_point = referenceSpace.boundsGeometry[i]; + assert_equals(valid_point.x, bounds_point.x); + assert_equals(bounds_point.y, 0.0); + assert_equals(valid_point.z, bounds_point.z); + assert_equals(bounds_point.w, 1.0); + } + }); + + resolve(); + }); + } + + // Now set the bounds explicitly and check again on the next frame. + fakeDeviceController.setBoundsGeometry(VALID_BOUNDS); + session.requestAnimationFrame(onFrame); + }); + }); +}; + +xr_session_promise_test(testName, testFunction, fakeDeviceInitParams, 'immersive-vr'); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html new file mode 100644 index 0000000..68f91fd3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webxr/xrStationaryReferenceSpace_floorlevel_updates.https.html
@@ -0,0 +1,70 @@ +<!DOCTYPE html> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src="resources/webxr_util.js"></script> +<script src="resources/webxr_test_constants.js"></script> +<script src="resources/xr-test-asserts.js"></script> +<canvas></canvas> + +<script> +let immersiveTestName = "'floor-level' XRStationaryReferenceSpace updates properly when " + + "the transform changes for immersive sessions"; +let nonImmersiveTestName = "'floor-level' XRStationaryReferenceSpace updates properly when " + + "the transform changes for non-immersive sessions"; + +let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; + +let testFunction = function(session, fakeDeviceController, t) { + // Don't need to request a frame/allow the stage updates to propagate before + // requesting local-floor because if the stage transform is set it just won't + // be emulated on the first frame, and we wait a frame before checking. + return session.requestReferenceSpace('local-floor') + .then((referenceSpace) => new Promise((resolve, reject) => { + function onFirstFrame(time, xrFrame) { + // On the first frame where the pose has been initialized, the stage + // should be using an emulated frame of reference because it has no + // stageParameters yet. So the pose should be ~1.5 meters off the floor. + t.step( () => { + let pose = xrFrame.getViewerPose(referenceSpace); + + let poseMatrix = pose.transform.matrix; + assert_approx_equals(poseMatrix[12], 0.0, FLOAT_EPSILON); + assert_greater_than(poseMatrix[13], 1.0); + assert_approx_equals(poseMatrix[14], 0.0, FLOAT_EPSILON); + + fakeDeviceController.setLocalToFloorLevelTransform(VALID_LOCAL_TO_FLOOR_TRANSFORM); + + // Need to request one animation frame for the new stage transform to + // propagate before we check that it's what we expect. + session.requestAnimationFrame(() => { + session.requestAnimationFrame(onFrame); + }); + }); + } + + function onFrame(time, xrFrame) { + t.step( () => { + // Check that stage transform was updated. + let pose = xrFrame.getViewerPose(referenceSpace); + assert_not_equals(pose, null); + + let poseMatrix = pose.transform.matrix; + assert_matrix_approx_equals(poseMatrix, VALID_LOCAL_TO_FLOOR_MATRIX, FLOAT_EPSILON); + }); + + // Finished. + resolve(); + } + + // Need to wait one frame for the removal to propagate before we check that + // everything is at the expected emulated position. + session.requestAnimationFrame(() => { + session.requestAnimationFrame(onFirstFrame); + }); + })); +}; + +xr_session_promise_test(immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr'); +xr_session_promise_test(nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline'); + +</script>
diff --git a/third_party/blink/web_tests/fast/css/containment/compositing-inputs-root-sticky-crash.html b/third_party/blink/web_tests/fast/css/containment/compositing-inputs-root-sticky-crash.html index 3602fab8..898e3fc 100644 --- a/third_party/blink/web_tests/fast/css/containment/compositing-inputs-root-sticky-crash.html +++ b/third_party/blink/web_tests/fast/css/containment/compositing-inputs-root-sticky-crash.html
@@ -1,14 +1,16 @@ <!DOCTYPE html> -<script> - if (window.testRunner) - testRunner.dumpAsText(); - onload = () => { - document.body.style.color = "blue"; - document.caretRangeFromPoint(); - document.body.removeChild(myul); - }; -</script> <p>The test passes if it doesn't crash in debug.</p> <ul id="myul" style="contain: strict; position: sticky; bottom: 0; overflow: scroll;"> <li id="myli" style="position: sticky;"></li> </ul> +<script> + if (window.testRunner) + testRunner.dumpAsText(); + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.body.style.color = "blue"; + document.caretRangeFromPoint(); + document.body.removeChild(myul); + }); + }); +</script>
diff --git a/third_party/blink/web_tests/fast/events/layout_change_after_update_hover.html b/third_party/blink/web_tests/fast/events/layout_change_after_update_hover.html new file mode 100644 index 0000000..4e88fb0 --- /dev/null +++ b/third_party/blink/web_tests/fast/events/layout_change_after_update_hover.html
@@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src='../../resources/gesture-util.js'></script> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> + +<style> +#blue { + background-color: rgb(0, 0, 255); + position: absolute; + left: 75px; + top: 75px; + height: 100px; + width: 100px; +} +#blue:hover { + top: 500px; +} +</style> +<div id="blue"></div> +<script> +const x = 100; +const y = 100; +var blue = document.getElementById('blue'); + +window.onload = async () => { + if (window.internals) { + internals.runtimeFlags.updateHoverFromLayoutChangeAtBeginFrameEnabled = true; + } + + promise_test(async () => { + await mouseMoveTo(x, y); + // Move the blue element away when it is hovered, so the blue element will oscillates + // between 75px and 500px at each animation frame. + assert_true(blue.matches(':hover'), "Hover on the blue element"); + assert_equals(blue.offsetTop, 500, "Check that the blue element is moved to 500px from the top"); + await raf(); + assert_false(blue.matches(':hover'), "The blue element is moved away from the mouse cursor after a begin frame"); + assert_equals(blue.offsetTop, 75, "The blue element is moved back to 75px from the top"); + await raf(); + assert_true(blue.matches(':hover'), "Hover on the blue element"); + assert_equals(blue.offsetTop, 500, "Check that the blue element is moved to 500px from the top"); + await raf(); + assert_false(blue.matches(':hover'), "The blue element is moved away from the mouse cursor after a begin frame"); + assert_equals(blue.offsetTop, 75, "The blue element is moved back to 75px from the top"); + }, 'The hover state is updated at the begin frame after the layout changes which is caused by hover update.'); + +} +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div-expected.txt b/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div-expected.txt deleted file mode 100644 index 6daf6c7..0000000 --- a/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div-expected.txt +++ /dev/null
@@ -1,10 +0,0 @@ - -PASS successfullyParsed is true - -TEST COMPLETE - -PASS event.wheelDeltaY is window.givenScrollTop*-3 -PASS event.wheelDeltaX is 0 -PASS event.wheelDelta is window.givenScrollTop*-3 -PASS div.scrollTop is window.expectedScrollTop -PASS div.scrollLeft is window.expectedScrollLeft
diff --git a/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div.html b/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div.html index 1c9afd5..a012c9b 100644 --- a/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div.html +++ b/third_party/blink/web_tests/fast/events/platform-wheelevent-paging-y-in-non-scrolling-div.html
@@ -1,63 +1,69 @@ -<html> - <head> - <script src="../../resources/js-test.js"></script> - <script> - var givenScrollTop = 2; - var givenScrollLeft = 0; - var expectedScrollTop = 0; - var expectedScrollLeft = 0; - var pixelsPerWheelTick = 40; - var event; - var div; +<!DOCTYPE html> +<script src='../../resources/testharness.js'></script> +<script src='../../resources/testharnessreport.js'></script> +<script> + const scrollDelta = 2; + const test = async_test("Page-based wheel scrolls provide correct delta " + + "in event handler, don't scroll if there's no " + + "overflow in direction."); - if (window.testRunner) - testRunner.waitUntilDone(); + if (window.testRunner) + testRunner.waitUntilDone(); - function dispatchWheelEvent() - { - var overflowElement = document.getElementById("overflow"); - if (overflowElement) - overflowElement.addEventListener("mousewheel", mousewheelHandler, false); + function mousewheelHandler(e) + { + test.step(() => { + const overflowElement = document.getElementById("overflow"); + assert_equals(e.wheelDeltaY, scrollDelta * -3); + assert_equals(e.wheelDeltaX, 0); + assert_equals(e.wheelDelta, scrollDelta * -3); + assert_equals(overflowElement.scrollTop, 0); + }); - if (window.eventSender) { - eventSender.mouseMoveTo(100, 110); - eventSender.continuousMouseScrollBy(-window.givenScrollLeft, -window.givenScrollTop, true); - } + test.done(); + } - setTimeout('checkOffsets();', 100); - } + window.onload = async () => { + const overflowElement = document.getElementById("overflow"); + overflowElement.addEventListener("mousewheel", mousewheelHandler, false); - function checkOffsets() - { - div = document.getElementById("overflow"); - shouldBe("div.scrollTop", "window.expectedScrollTop"); - shouldBe("div.scrollLeft", "window.expectedScrollLeft"); + // TODO(bokan): This should really be using gpuBenchmarking or similar to + // ensure the wheels are injected at the browser level, however, + // gpuBenchmarking today doesn't support "page" granularity wheels. + if (window.eventSender) { + eventSender.mouseMoveTo(100, 110); + eventSender.continuousMouseScrollBy(0, -scrollDelta, true); + } + }; +</script> +<style> + body { + margin: 0; + } - if (window.testRunner) - testRunner.notifyDone(); - } + #overflow { + border:2px solid black; + overflow:auto; + white-space:nowrap; + height:200px; + width:200px; + } - function mousewheelHandler(e) - { - event = e; - shouldBe("event.wheelDeltaY", "window.givenScrollTop*-3"); - shouldBe("event.wheelDeltaX", "0"); + #container { + height: 185px; + width: 600px; + } - if (e.wheelDeltaY) - shouldBe("event.wheelDelta", "window.givenScrollTop*-3"); - else - shouldBe("event.wheelDelta", "0"); - } - </script> - </head> + .box { + height:181px; + width:300px; + display:inline-block; + } +</style> - <body style="margin:0" onload="setTimeout('dispatchWheelEvent();', 100)"> - <div id="overflow" style="border:2px solid black;overflow:auto;white-space:nowrap;height:200px;width:200px;"> - <div style="height:185px;width:600px;"> - <div style="border:0px;background-color:red;height:181px;width:300px;display:inline-block;"></div> - <div style="border:0px;background-color:green;height:181px;width:300px;display:inline-block;"></div> - </div> - </div> - <div id="console"></div> - </body> -</html> +<div id="overflow"> + <div id="container"> + <div class="box" style="background-color:red"></div> + <div class="box" style="background-color:green"></div> + </div> +</div>
diff --git a/third_party/blink/web_tests/fast/events/scale-and-scroll-div-expected.txt b/third_party/blink/web_tests/fast/events/scale-and-scroll-div-expected.txt deleted file mode 100644 index 54639d5..0000000 --- a/third_party/blink/web_tests/fast/events/scale-and-scroll-div-expected.txt +++ /dev/null
@@ -1,20 +0,0 @@ -This tests that a div scrolled by gesture touch while the page is scaled still scrolls at the rate of the touch - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - -PASS successfullyParsed is true - -TEST COMPLETE - -Scrolling unscaled div -PASS scrollBox.scrollTop is within 0.1 of 10 -PASS scrollBox.scrollTop is within 0.1 of 10 -Scrolling 1.5 scaled div -PASS scrollBox.scrollTop is within 0.1 of 6 -PASS scrollBox.scrollTop is within 0.1 of 13 -Scrolling 1.63 scaled div -PASS scrollBox.scrollTop is within 0.1 of 6 -PASS scrollBox.scrollTop is within 0.1 of 13 -Scrolling 2.0 scaled div -PASS scrollBox.scrollTop is within 0.1 of 5 -PASS scrollBox.scrollTop is within 0.1 of 15
diff --git a/third_party/blink/web_tests/fast/events/scale-and-scroll-div.html b/third_party/blink/web_tests/fast/events/scale-and-scroll-div.html index 2dd1ab5..79d54775 100644 --- a/third_party/blink/web_tests/fast/events/scale-and-scroll-div.html +++ b/third_party/blink/web_tests/fast/events/scale-and-scroll-div.html
@@ -1,122 +1,104 @@ <!DOCTYPE html> -<html> -<head> - <style> - ::-webkit-scrollbar { - width: 0px; - height: 0px; - } - #bluebox { - width: 100px; - height: 100px; - background: blue; - padding: 0px; - margin: 0px; - } - #redbox { - width: 100px; - height: 100px; - background: red; - padding: 0px; - margin: 0px; - } - </style> - <script> +<script src='../../resources/testharness.js'></script> +<script src='../../resources/testharnessreport.js'></script> +<script src='../../resources/gesture-util.js'></script> +<style> + ::-webkit-scrollbar { + width: 0px; + height: 0px; + } + #bluebox { + width: 100px; + height: 100px; + background: blue; + padding: 0px; + margin: 0px; + } + #redbox { + width: 100px; + height: 100px; + background: red; + padding: 0px; + margin: 0px; + } +</style> +<script> + const floatPrecision = 0.1; + const scrollDistance = 10; - var floatPrecision = 0.1; - var scrollDistance = 10; - var expectedScrollDistance; - var scrollBox; - var integerScrollOffset = true; + async function scroll(scaleFactor, msg) { + const scrollBox = document.getElementById("scrollbox"); + let expectedScrollDistance; - function scroll(scaleFactor) { - if (integerScrollOffset) - expectedScrollDistance = Math.floor(scrollDistance/scaleFactor); - else - expectedScrollDistance = scrollDistance/scaleFactor; + if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) + expectedScrollDistance = scrollDistance/scaleFactor; + else + expectedScrollDistance = Math.floor(scrollDistance/scaleFactor); - scrollBox.scrollTop = 0; + scrollBox.scrollTop = 0; - eventSender.gestureScrollBegin(10, 100); - for(var i = 0; i < scrollDistance; ++i) { - eventSender.gestureScrollUpdate(0, -1); - } - eventSender.gestureScrollEnd(0, 0); + let location = { x: 10, y: 100 }; + await smoothScroll(scrollDistance, + location.x, + location.y, + GestureSourceType.TOUCH_INPUT, + 'down', + SPEED_INSTANT); - shouldBeCloseTo('scrollBox.scrollTop', expectedScrollDistance, floatPrecision); + assert_approx_equals(scrollBox.scrollTop, + expectedScrollDistance, + floatPrecision, + msg + " - scroll down."); - scrollBox.scrollTop = 20; + scrollBox.scrollTop = 20; - eventSender.gestureScrollBegin(10, 300); - for(var i = 0; i < scrollDistance; ++i) { - eventSender.gestureScrollUpdate(0, 1); - } - eventSender.gestureScrollEnd(0, 0); + location = { x: 10, y: 300 }; + await smoothScroll(scrollDistance, + location.x, + location.y, + GestureSourceType.TOUCH_INPUT, + 'up', + SPEED_INSTANT); - if (integerScrollOffset) - expectedScrollDistance = Math.floor(20 - scrollDistance/scaleFactor); - else - expectedScrollDistance = 20 - scrollDistance/scaleFactor; + if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) + expectedScrollDistance = 20 - scrollDistance/scaleFactor; + else + expectedScrollDistance = Math.floor(20 - scrollDistance/scaleFactor); - shouldBeCloseTo('scrollBox.scrollTop', expectedScrollDistance, floatPrecision); - } + assert_approx_equals(scrollBox.scrollTop, + expectedScrollDistance, + floatPrecision, + msg + " - scroll up."); + } - function scaleWithEventSender(scaleFactor) { - if (window.internals) { - internals.setPageScaleFactor(scaleFactor); - } - } + window.onload = () => { + promise_test(async () => { + window.internals.setPageScaleFactor(1.0); + await scroll(1.0, "Scrolling unscaled div"); - function test() { - scrollBox = document.getElementById("scrollbox"); + window.internals.setPageScaleFactor(1.5); + await scroll(1.5, "Scrolling 1.5 scaled div"); - debug('Scrolling unscaled div'); - scaleWithEventSender(1.0); - scroll(1.0); - debug('Scrolling 1.5 scaled div'); - scaleWithEventSender(1.5); - scroll(1.5); - debug('Scrolling 1.63 scaled div'); - scaleWithEventSender(1.63); - scroll(1.63); - debug('Scrolling 2.0 scaled div'); - scaleWithEventSender(2.0); - scroll(2.0); - } + window.internals.setPageScaleFactor(1.63); + await scroll(1.63, "Scrolling 1.63 scaled div"); - function run_test() { - if (window.eventSender && window.internals) { - description('This tests that a div scrolled by gesture touch while the page is scaled still scrolls at the rate of the touch'); + window.internals.setPageScaleFactor(2.0); + await scroll(2.0, "Scrolling 2.0 scaled div"); + }, "div touch scrolled under pinch-zoom scrolls at correct rate"); - if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) { - internals.settings.setPreferCompositingToLCDTextEnabled(true); - integerScrollOffset = false; - } - test(); - - } else { - debug('eventSender not detected. Not running test.'); - } - } - </script> - <script src="../../resources/js-test.js"></script> -</head> -<body onload="run_test();"> - <div id="scrollbox" style="left:0; top:0; width:500px; height:500px; position:absolute; overflow-y: scroll; overflow-x: scroll;"> - <div id="bluebox"></div> - <div id="redbox"></div> - <div id="bluebox"></div> - <div id="redbox"></div> - <div id="bluebox"></div> - <div id="redbox"></div> - <div id="bluebox"></div> - <div id="redbox"></div> - <div id="bluebox"></div> - <div id="redbox"></div> - <div id="bluebox"></div> - <div id="redbox"></div> - </div> - - <div id="console"></div> -</body> -</html> + }; +</script> +<div id="scrollbox" style="left:0; top:0; width:500px; height:500px; position:absolute; overflow-y: scroll; overflow-x: scroll;"> + <div id="bluebox"></div> + <div id="redbox"></div> + <div id="bluebox"></div> + <div id="redbox"></div> + <div id="bluebox"></div> + <div id="redbox"></div> + <div id="bluebox"></div> + <div id="redbox"></div> + <div id="bluebox"></div> + <div id="redbox"></div> + <div id="bluebox"></div> + <div id="redbox"></div> +</div>
diff --git a/third_party/blink/web_tests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html b/third_party/blink/web_tests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html deleted file mode 100644 index 5fd035da..0000000 --- a/third_party/blink/web_tests/fast/events/touch/gesture/touch-gesture-scroll-div-scaled.html +++ /dev/null
@@ -1,110 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<script src="../../../../resources/testharness.js"></script> -<script src="../../../../resources/testharnessreport.js"></script> -<script src="../../../../resources/gesture-util.js"></script> -<style type="text/css"> -#touchtarget { - width: 100px; - height: 100px; - position: relative; - background: white; -} - -::-webkit-scrollbar { - width: 0px; - height: 0px; -} - -#movingbox { - width: 100%; - height: 100%; - position: absolute; - word-wrap: break-word; - overflow-y: scroll; - overflow-x: scroll; - display: block; -} - -#greenbox { - width: 100px; - height: 100px; - background: green; - padding: 0px; - margin: 0px; -} - -#redbox { - width: 100px; - height: 100px; - background: red; - padding: 0px; - margin: 0px; -} - -td { - padding: 0px; -} -</style> -<body style="margin:0"> -<div id="touchtarget"> - <div id="movingbox"> - <table border="0" cellspacing="0px" id="tablefoo"> - <tr> - <td><div id="redbox"></div></td> - <td><div id="greenbox"></div></td> - </tr> - <tr> - <td><div id="greenbox"></div></td> - <td><div id="greenbox"></div></td> - </tr> - </table> - </div> -</div> -</body> - -<script type="text/javascript"> -var movingdiv = document.getElementById('movingbox'); -var expectedGesturesTotal = 2; -var gesturesOccurred = 0; -var scrollAmountX = [0, 0]; -var scrollAmountY = [0, 0]; -var scrollEventsOccurred = 0; -var expectedScrollEventsOccurred = 2; -var scrolledElement = movingdiv; - -var x = 95; -var y = 12; -function firstGestureScroll() { - return smoothScroll(90, x, y, GestureSourceType.TOUCH_INPUT, "right", - SPEED_INSTANT); -} - -function secondGestureScroll() { - x = 12; - y = 97; - return smoothScroll(95, x, y, GestureSourceType.TOUCH_INPUT, "down", - SPEED_INSTANT); -} - -promise_test (async () => { - var scaleFactor = 2.0; - if (window.internals) - internals.setPageScaleFactor(scaleFactor); - - movingdiv.addEventListener("scroll", recordScroll); - - if (internals.runtimeFlags.fractionalScrollOffsetsEnabled) { - internals.settings.setPreferCompositingToLCDTextEnabled(true); - scrollAmountX = [90, 90]; - scrollAmountY = [47.5, 95]; - } else { - scrollAmountX = [45, 45]; - scrollAmountY = [0, 47]; - } - - await firstGestureScroll(); - await waitFor(checkScrollOffset); - await secondGestureScroll(); - await waitFor(checkScrollOffset); -}, 'This tests gesture event scrolling of an overflow div with page scale.'); -</script>
diff --git a/third_party/blink/web_tests/fast/history/scroll-restoration/scroll-restoration-fragment-navigation-samedoc.html b/third_party/blink/web_tests/fast/history/scroll-restoration/scroll-restoration-fragment-navigation-samedoc.html index 01ce213..51d1ad5 100644 --- a/third_party/blink/web_tests/fast/history/scroll-restoration/scroll-restoration-fragment-navigation-samedoc.html +++ b/third_party/blink/web_tests/fast/history/scroll-restoration/scroll-restoration-fragment-navigation-samedoc.html
@@ -24,7 +24,7 @@ <body tabindex=1> <div id='log'></div> <div id='focus-target' tabindex=1></div> - <a id='fragment' name='fragment' class='box' href='#'>Anchor</a> + <div id='fragment' href='#'>Target</div> </body> <script src="../../../resources/testharness.js"></script> @@ -60,4 +60,4 @@ }, 0); }, 'scrollRestoration takes precedent over scrolling to fragment'); -</script> \ No newline at end of file +</script>
diff --git a/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-no-tracks.html b/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-no-tracks.html new file mode 100644 index 0000000..6fae5c3 --- /dev/null +++ b/third_party/blink/web_tests/fast/mediarecorder/MediaRecorder-no-tracks.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src=../../resources/testharness.js></script> +<script src=../../resources/testharnessreport.js></script> +<script> +// Check that trying to start a recording from a stream without tracks fails. +test(function() { + const recorder = new MediaRecorder(new MediaStream()); + assert_throws({name: "UnknownError"}, () => recorder.start(), "DOMException"); + // Try again to verify that it fails the same way. + assert_throws({name: "UnknownError"}, () => recorder.start(), "DOMException"); +}, 'Trying to record a stream without tracks fails'); + +</script>
diff --git a/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited-expected.html b/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited-expected.html deleted file mode 100644 index bb886c4..0000000 --- a/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited-expected.html +++ /dev/null
@@ -1,32 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<style> -#settings { - width: 100px; - height: 100px; - position: fixed; - top: 10px; - z-index:2; - background-color:red; -} - -#header { - width:200px; - height:20px; - position:fixed; - right:100px; - top:100px; - z-index:3; - background-color:green; -} -</style> -</head> - -<body style="width:1000px;height:2000px;"> -<div id="settings" > - <div id="header">scroll offset is 1 - </div> -</div> -</body> -</html>
diff --git a/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html b/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html deleted file mode 100644 index f58727a..0000000 --- a/third_party/blink/web_tests/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html +++ /dev/null
@@ -1,41 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<style> -#settings { - width: 100px; - height: 100px; - position: fixed; - top: 10px; - z-index:2; - background-color:red; -} - -#header { - width:200px; - height:20px; - position:fixed; - right:100px; - top:100px; - z-index:3; - background-color:green; -} -</style> -</head> - -<script> -function runTest() -{ - internals.settings.setPreferCompositingToLCDTextEnabled(false); - window.scrollTo(0, 1.5); - document.getElementById('header').innerHTML = "scroll offset is " + window.scrollY; -} -</script> - -<body style="width:1000px;height:2000px;" onload="runTest()"> -<div id="settings" > - <div id="header"> - </div> -</div> -</body> -</html>
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt new file mode 100644 index 0000000..27cd2a5 --- /dev/null +++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=CompositeAfterPaint/compositing/overflow/scrollbar-layer-placement-negative-z-index-child-expected.txt
@@ -0,0 +1,44 @@ +{ + "layers": [ + { + "name": "Scrolling background of LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutNGBlockFlow (positioned) DIV class='outer'", + "position": [-21, -21], + "bounds": [352, 294], + "transform": 1 + }, + { + "name": "LayoutNGBlockFlow DIV class='scroller'", + "position": [218, 5], + "bounds": [7, 160], + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [21, 21, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [41, 41, 0, 1] + ] + } + ] +} +
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/audits/get-encoded-response.js b/third_party/blink/web_tests/http/tests/inspector-protocol/audits/get-encoded-response.js index e90d7cc..7d9dbcc1 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/audits/get-encoded-response.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/audits/get-encoded-response.js
@@ -11,7 +11,7 @@ async function logResponse(url, encoding, quality, sizeOnly) { testRunner.log(`\nResults for ${url} encoding=${encoding} q=${quality} sizeOnly=${sizeOnly}`); - session.evaluate(`fetch(${JSON.stringify(url)})`); + session.evaluate(`fetch(${JSON.stringify(url)}).then(r => r.text())`); const requestId = (await dp.Network.onceResponseReceived()).params.requestId; const result = (await dp.Audits.getEncodedResponse({requestId, encoding, quality, sizeOnly})).result;
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network-data-length.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network-data-length.js index 7a74640..16ec1e5 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network-data-length.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network-data-length.js
@@ -10,7 +10,7 @@ var pendingRequests = 0; function sendRequest(url) { - dp.Runtime.evaluate({expression: `fetch('${url}')`}); + dp.Runtime.evaluate({expression: `fetch('${url}').then(r => r.text())`}); pendingRequests++; }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/get-response-body.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/get-response-body.js index 7090d712..22a249ec 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/get-response-body.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/get-response-body.js
@@ -5,7 +5,7 @@ await dp.Network.enable(); async function logResponseBody(url) { - session.evaluate(`fetch(${JSON.stringify(url)});`); + session.evaluate(`fetch(${JSON.stringify(url)}).then(r => r.text());`); var requestWillBeSent = (await dp.Network.onceRequestWillBeSent()).params; testRunner.log(`Request for ${requestWillBeSent.request.url}`);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-both-redirect-rewrite.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-both-redirect-rewrite.js index 74aaffa..d358796 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-both-redirect-rewrite.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-both-redirect-rewrite.js
@@ -18,7 +18,7 @@ ]}); testRunner.log('Request interception patterns sent.'); - session.evaluate(`fetch('${testRunner.url('../resources/redirect1.php')}')`); + session.evaluate(`fetch('${testRunner.url('../resources/redirect1.php')}').then(r => r.text())`); await waitForInterceptionEventAndContinue("/redirect1.php"); await waitForInterceptionEventAndContinue("/redirect1.php");
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-response-redirect.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-response-redirect.js index 8cab1976..eef47d0 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-response-redirect.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-on-response-redirect.js
@@ -65,7 +65,7 @@ await new Promise(resolve => { session.protocol.Network.onResponseReceived(resolve); session.evaluate(` - fetch('${testRunner.url('../resources/ping-redirect.php')}'); + fetch('${testRunner.url('../resources/ping-redirect.php')}').then(r => r.text()); `); });
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-patterns.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-patterns.js index 264d581..1ad5b0b 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-patterns.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/request-interception-patterns.js
@@ -73,11 +73,11 @@ */ async function testUrls() { requestInterceptionWaitingMap.clear(); - session.evaluate(`fetch('../network/resources/small-test-1.txt')`); + session.evaluate(`fetch('../network/resources/small-test-1.txt').then(r => r.text())`); await new Promise(resolve => responseWasReceivedCallback = resolve); - session.evaluate(`fetch('../network/resources/small-test-2.txt')`); + session.evaluate(`fetch('../network/resources/small-test-2.txt').then(r => r.text())`); await new Promise(resolve => responseWasReceivedCallback = resolve); - session.evaluate(`fetch('../resources/test-page.html')`); + session.evaluate(`fetch('../resources/test-page.html').then(r => r.text())`); await new Promise(resolve => responseWasReceivedCallback = resolve); testRunner.log(''); }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/search-in-response.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/search-in-response.js index 02b6c470..03c6b0f3 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/search-in-response.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/search-in-response.js
@@ -4,7 +4,7 @@ await dp.Network.enable(); var url = testRunner.url('./resources/final.js'); - session.evaluate(`fetch("${url}");`); + session.evaluate(`fetch("${url}").then(r => r.text());`); var requestWillBeSent = (await dp.Network.onceRequestWillBeSent()).params; testRunner.log(`Request for ${requestWillBeSent.request.url}`);
diff --git a/third_party/blink/web_tests/transforms/selection-bounds-in-transformed-view.html b/third_party/blink/web_tests/transforms/selection-bounds-in-transformed-view.html index 79e84cc..70bf438 100644 --- a/third_party/blink/web_tests/transforms/selection-bounds-in-transformed-view.html +++ b/third_party/blink/web_tests/transforms/selection-bounds-in-transformed-view.html
@@ -33,8 +33,8 @@ internals.settings.setPreferCompositingToLCDTextEnabled( true); document.execCommand("FindString", false, "target"); - assert_equals(window.visualViewport.pageLeft, 167); - assert_equals(window.visualViewport.pageTop, 2867); + assert_approx_equals(window.visualViewport.pageLeft, 167, 1); + assert_approx_equals(window.visualViewport.pageTop, 2867, 1); }, "Test scrolled into view with prefer compositing enabled"); @@ -43,8 +43,8 @@ internals.settings.setPreferCompositingToLCDTextEnabled( false); document.execCommand("FindString", false, "target"); - assert_equals(window.visualViewport.pageLeft, 167); - assert_equals(window.visualViewport.pageTop, 2867); + assert_approx_equals(window.visualViewport.pageLeft, 167, 1); + assert_approx_equals(window.visualViewport.pageTop, 2867, 1); }, "Test scrolled into view with prefer compositing disabled"); } </script>
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt index 42df37b..0835ba5 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-properties-as-js-properties-expected.txt
@@ -239,6 +239,8 @@ overflowX overflowY overscrollBehavior +overscrollBehaviorBlock +overscrollBehaviorInline overscrollBehaviorX overscrollBehaviorY padding
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt index a72490f..6add93f 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/css-property-listing-expected.txt
@@ -249,6 +249,8 @@ overflow-wrap overflow-x overflow-y + overscroll-behavior-block + overscroll-behavior-inline overscroll-behavior-x overscroll-behavior-y padding-block-end
diff --git a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt index a18be58..243828b 100644 --- a/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt +++ b/third_party/blink/web_tests/webexposed/css-properties-as-js-properties-expected.txt
@@ -264,6 +264,8 @@ overflowX overflowY overscrollBehavior +overscrollBehaviorBlock +overscrollBehaviorInline overscrollBehaviorX overscrollBehaviorY padding
diff --git a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt index 1f372d53..dfb2a45 100644 --- a/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/css-property-listing-expected.txt
@@ -261,6 +261,8 @@ overflow-wrap overflow-x overflow-y + overscroll-behavior-block + overscroll-behavior-inline overscroll-behavior-x overscroll-behavior-y padding-block-end
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/get-bounding-client-rect-block-layout-in-iframe.html b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/get-bounding-client-rect-block-layout-in-iframe.html new file mode 100644 index 0000000..bdab4eb6 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/display-lock/lock-after-append/get-bounding-client-rect-block-layout-in-iframe.html
@@ -0,0 +1,68 @@ +<!doctype HTML> +<html> +<meta charset="utf8"> +<title>Display Locking: getBoundingClientRect on block layout</title> +<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org"> +<link rel="help" href="https://github.com/WICG/display-locking"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +#container { + contain: style layout; + width: 123px; + height: 234px; +} +#frame { + padding: 0; + margin: 0; + width: 500px; + height: 500px; +} +</style> +<body> +<div id="container"> + <iframe id="frame" frameborder=0 srcdoc=' + <style> + body { + padding: 0; + margin: 0; + } + #target { + background: lightgreen; + width: 50%; + height: 50px; + } + </style> + <div id="target"></div> + '></iframe> +</div> + +<script> +let load_promise = new Promise((resolve) => { + window.onload = resolve; +}); + +async_test(async(t) => { + await load_promise; + + const container = document.getElementById("container"); + await container.displayLock.acquire({ timeout: Infinity }); + + const frame = document.getElementById("frame"); + frame.style.width = "224px"; + frame.style.height = "248px"; + + const target = frame.contentDocument.getElementById("target"); + t.step(() => assert_true(!!target, "sanity check that target exists")); + let rect = target.getBoundingClientRect(); + t.step(() => assert_equals(rect.width, 112, "target uses update frame size for width")); + t.step(() => assert_equals(rect.height, 50, "target uses set size for height")); + + t.done(); +}, "getBoundingClientRect in iframe"); + +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/xr/events_referenceSpace_reset.html b/third_party/blink/web_tests/xr/events_referenceSpace_reset.html deleted file mode 100644 index 9f09711f..0000000 --- a/third_party/blink/web_tests/xr/events_referenceSpace_reset.html +++ /dev/null
@@ -1,59 +0,0 @@ -<!DOCTYPE html> -<script src="../resources/testharness.js"></script> -<script src="../resources/testharnessreport.js"></script> -<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> -<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script> -<script src="../external/wpt/resources/chromium/webxr-test.js"></script> -<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script> -<script src="../xr/resources/xr-internal-device-mocking.js"></script> -<script src="../xr/resources/xr-test-utils.js"></script> -<canvas id="webgl-canvas"></canvas> - -<script> -let testName = - "XRSession resetpose from a device properly fires off the right events" - -let watcherDone = new Event("watcherdone"); - -let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; - -let requestSessionModes = [ - 'inline', - 'immersive-vr', -]; - -let testFunction = function(session, t, fakeDeviceController) { - // TODO(981003): Because the device gets re-used we have to ensure that the - // resetPose bit is cleared. - fakeDeviceController.setResetPose(false); - // Session must have a baseLayer or frame requests will be ignored. - session.updateRenderState({ - baseLayer: new XRWebGLLayer(session, gl, { - compositionDisabled: session.mode == 'inline' }) - }); - - let resetPromise = session.requestReferenceSpace('local') - .then((refSpace) => { - let eventWatcher = new EventWatcher( - t, refSpace, ["reset", "watcherdone"]); - refSpace.addEventListener("reset", (event) => { - assert_equals(event.referenceSpace, refSpace); - refSpace.dispatchEvent(watcherDone); - }, false); - return eventWatcher.wait_for(["reset", "watcherdone"]); - }); - - fakeDeviceController.setResetPose(true); - - // Trigger the reset pose. - session.requestAnimationFrame(() => { - session.requestAnimationFrame(() => {}); - }); - - return resetPromise; -}; - -xr_session_promise_test( - testFunction, fakeDeviceInitParams, requestSessionModes, testName); - -</script>
diff --git a/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js b/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js index a9942d7..ef1a2164 100644 --- a/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js +++ b/third_party/blink/web_tests/xr/resources/xr-internal-device-mocking.js
@@ -4,18 +4,6 @@ * for interal tests. The main mocked objects are found in * ../external/wpt/resources/chromium/webxr-test.js. */ -let default_standing = new gfx.mojom.Transform(); -default_standing.matrix = [1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 1.65, 0, 1]; -const default_stage_parameters = { - standingTransform: default_standing, - sizeX: 1.5, - sizeZ: 1.5, - bounds: null -}; - MockRuntime.prototype.base_getFrameData = MockRuntime.prototype.getFrameData; MockRuntime.prototype.getFrameData = function() { @@ -72,27 +60,6 @@ return Promise.resolve(hit_results); }; -MockRuntime.prototype.setResetPose = function(to) { - if (this.pose_) { - this.pose_.poseReset = to; - } -}; - -MockRuntime.prototype.setStageTransform = function(value) { - if (value) { - if (!this.displayInfo_.stageParameters) { - this.displayInfo_.stageParameters = default_stage_parameters; - } - - this.displayInfo_.stageParameters.standingTransform = new gfx.mojom.Transform(); - this.displayInfo_.stageParameters.standingTransform.matrix = value; - } else if (this.displayInfo_.stageParameters) { - this.displayInfo_.stageParameters = null; - } - - this.sessionClient_.onChanged(this.displayInfo_); -}; - MockRuntime.prototype.setStageSize = function(x, z) { if (!this.displayInfo_.stageParameters) { this.displayInfo_.stageParameters = default_stage_parameters; @@ -104,16 +71,6 @@ this.sessionClient_.onChanged(this.displayInfo_); }; -MockRuntime.prototype.setStageBounds = function(value) { - if (!this.displayInfo_.stageParameters) { - this.displayInfo_.stageParameters = default_stage_parameters; - } - - this.displayInfo_.stageParameters.bounds = value; - - this.sessionClient_.onChanged(this.displayInfo_); -}; - MockRuntime.prototype.getSubmitFrameCount = function() { return this.presentation_provider_.submit_frame_count_; };
diff --git a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html b/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html index 806a653e..0fe818a 100644 --- a/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html +++ b/third_party/blink/web_tests/xr/xrBoundedReferenceSpace_updates.html
@@ -13,7 +13,12 @@ let testName = "'XRBoundedReferenceSpace updates properly when the changes are applied"; -let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; +let fakeDeviceInitParams = { + supportsImmersive: true, + views: VALID_VIEWS, + viewerOrigin: IDENTITY_TRANSFORM, + localToFloorLevelTransform: VALID_LOCAL_TO_FLOOR_TRANSFORM +}; let requestSessionModes = [ 'immersive-vr', @@ -25,7 +30,8 @@ baseLayer: new XRWebGLLayer(session, gl) }); - fakeDeviceController.setStageTransform(VALID_STAGE_TRANSFORM); + // If the array bounds aren't set, but the stage size is, then Chrome should + // fake the stage size. fakeDeviceController.setStageSize(2.0, 3.0); return new Promise((resolve, reject) => { @@ -42,7 +48,7 @@ assert_not_equals(pose, null); let poseMatrix = pose.transform.matrix; - assert_matrices_approx_equal(poseMatrix, VALID_STAGE_TRANSFORM); + assert_matrices_approx_equal(poseMatrix, VALID_LOCAL_TO_FLOOR_MATRIX); // If an explicit array of bounds points was not provided then the // bounds geometry should represent the four corners of the rectangle @@ -58,7 +64,7 @@ }); // Now set the bounds explicitly and check again on the next frame. - fakeDeviceController.setStageBounds(VALID_BOUNDS); + fakeDeviceController.setBoundsGeometry(VALID_BOUNDS); session.requestAnimationFrame(onFrame); } @@ -71,9 +77,9 @@ let valid_point = VALID_BOUNDS[i]; let bounds_point = referenceSpace.boundsGeometry[i]; assert_equals(valid_point.x, bounds_point.x); - assert_equals(point.y, 0.0); + assert_equals(bounds_point.y, 0.0); assert_equals(valid_point.z, bounds_point.z); - assert_equals(point.w, 1.0); + assert_equals(bounds_point.w, 1.0); } });
diff --git a/third_party/blink/web_tests/xr/xrReferenceSpace_originOffsetBounded.html b/third_party/blink/web_tests/xr/xrReferenceSpace_originOffsetBounded.html index de36ea92..d7661333 100644 --- a/third_party/blink/web_tests/xr/xrReferenceSpace_originOffsetBounded.html +++ b/third_party/blink/web_tests/xr/xrReferenceSpace_originOffsetBounded.html
@@ -32,11 +32,18 @@ resolution: VALID_RESOLUTION }]; +const FLOOR_TRANSFORM = { + position: [0.1, 0.2, 0.3], + orientation: [0, 0, 0, 1] +}; + const fakeDeviceInitParams = { supportsImmersive: true, views: VIEWS_WITH_OFFSET, - viewerOrigin: IDENTITY_TRANSFORM + viewerOrigin: IDENTITY_TRANSFORM, + localToFloorLevelTransform: FLOOR_TRANSFORM }; + const requestSessionModes = ['immersive-vr']; function testFunction(session, t, fakeDeviceController) { @@ -58,14 +65,6 @@ 0.01, 0.02, 0.03, 1, ]; - const STAGE_TRANSFORM = [ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0.1, 0.2, 0.3, 1, - ]; - - fakeDeviceController.setStageTransform(STAGE_TRANSFORM); fakeDeviceController.setStageSize(2.0, 3.0); let input_source = new MockXRInputSource();
diff --git a/third_party/blink/web_tests/xr/xrStationaryReferenceSpace_floorlevel_updates.html b/third_party/blink/web_tests/xr/xrStationaryReferenceSpace_floorlevel_updates.html deleted file mode 100644 index a255dee..0000000 --- a/third_party/blink/web_tests/xr/xrStationaryReferenceSpace_floorlevel_updates.html +++ /dev/null
@@ -1,86 +0,0 @@ -<!DOCTYPE html> -<script src="../resources/testharness.js"></script> -<script src="../resources/testharnessreport.js"></script> -<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script> -<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script> -<script src="../external/wpt/resources/chromium/webxr-test.js"></script> -<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script> -<script src="../xr/resources/xr-internal-device-mocking.js"></script> -<script src="../xr/resources/xr-test-utils.js"></script> -<canvas id="webgl-canvas"></canvas> - -<script> -let testName = - "'floor-level' XRStationaryReferenceSpace updates properly when the transform changes"; - -let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; - -let requestSessionModes = [ - 'inline', - 'immersive-vr', -]; - -let testFunction = function(session, t, fakeDeviceController) { - // Session must have a baseLayer or else frame requests will be ignored. - session.updateRenderState({ - baseLayer: new XRWebGLLayer(session, gl, { - compositionDisabled: session.mode == 'inline' }) - }); - - // TODO(981003): Because the device gets re-used we have to ensure that the - //stage transform is cleared here, it shouldn't be set from the initial device. - fakeDeviceController.setStageTransform(null); - - // Don't need to request a frame/allow the stage updates to propagate before - // requesting local-floor because if the stage transform is set it just won't - // be emulated on the first frame, and we wait a frame before checking. - return session.requestReferenceSpace('local-floor') - .then((referenceSpace) => new Promise((resolve, reject) => { - function onFirstFrame(time, xrFrame) { - // On the first frame where the pose has been initialized, the stage - // should be using an emulated frame of reference because it has no - // stageParameters yet. So the pose should be ~1.5 meters off the floor. - t.step( () => { - let pose = xrFrame.getViewerPose(referenceSpace); - - let poseMatrix = pose.transform.matrix; - assert_approx_equals(poseMatrix[12], 0.0, FLOAT_EPSILON); - assert_greater_than(poseMatrix[13], 1.0); - assert_approx_equals(poseMatrix[14], 0.0, FLOAT_EPSILON); - - fakeDeviceController.setStageTransform(VALID_STAGE_TRANSFORM); - - // Need to request one animation frame for the new stage transform to - // propagate before we check that it's what we expect. - session.requestAnimationFrame(() => { - session.requestAnimationFrame(onFrame); - }); - }); - } - - function onFrame(time, xrFrame) { - t.step( () => { - // Check that stage transform was updated. - let pose = xrFrame.getViewerPose(referenceSpace); - assert_not_equals(pose, null); - - let poseMatrix = pose.transform.matrix; - assert_matrices_approx_equal(poseMatrix, VALID_STAGE_TRANSFORM); - }); - - // Finished. - resolve(); - } - - // Need to wait one frame for the removal to propagate before we check that - // everything is at the expected emulated position. - session.requestAnimationFrame(() => { - session.requestAnimationFrame(onFirstFrame); - }); - })); -}; - -xr_session_promise_test( - testFunction, fakeDeviceInitParams, requestSessionModes, testName); - -</script>
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json index dfa9461..f44cbde 100644 --- a/third_party/polymer/v1_0/bower.json +++ b/third_party/polymer/v1_0/bower.json
@@ -31,7 +31,6 @@ "iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#2.1.0", "neon-animation": "PolymerElements/neon-animation#2.1.0", "paper-behaviors": "PolymerElements/paper-behaviors#2.1.0", - "paper-button": "PolymerElements/paper-button#2.0.0", "paper-input": "PolymerElements/paper-input#2.2.2", "paper-progress": "PolymerElements/paper-progress#2.0.1", "paper-ripple": "PolymerElements/paper-ripple#2.0.1",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-behaviors/BUILD.gn b/third_party/polymer/v1_0/components-chromium/paper-behaviors/BUILD.gn index 4541ea92..ee3258f 100644 --- a/third_party/polymer/v1_0/components-chromium/paper-behaviors/BUILD.gn +++ b/third_party/polymer/v1_0/components-chromium/paper-behaviors/BUILD.gn
@@ -6,13 +6,6 @@ import("//third_party/closure_compiler/compile_js.gni") -js_library("paper-button-behavior-extracted") { - deps = [ - ":paper-ripple-behavior-extracted", - "../iron-behaviors:iron-button-state-extracted", - ] -} - js_library("paper-ripple-behavior-extracted") { deps = [ "../iron-behaviors:iron-button-state-extracted",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior-extracted.js deleted file mode 100644 index c02810ab..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior-extracted.js +++ /dev/null
@@ -1,81 +0,0 @@ -/** @polymerBehavior Polymer.PaperButtonBehavior */ - Polymer.PaperButtonBehaviorImpl = { - properties: { - /** - * The z-depth of this element, from 0-5. Setting to 0 will remove the - * shadow, and each increasing number greater than 0 will be "deeper" - * than the last. - * - * @attribute elevation - * @type number - * @default 1 - */ - elevation: { - type: Number, - reflectToAttribute: true, - readOnly: true - } - }, - - observers: [ - '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', - '_computeKeyboardClass(receivedFocusFromKeyboard)' - ], - - hostAttributes: { - role: 'button', - tabindex: '0', - animated: true - }, - - _calculateElevation: function() { - var e = 1; - if (this.disabled) { - e = 0; - } else if (this.active || this.pressed) { - e = 4; - } else if (this.receivedFocusFromKeyboard) { - e = 3; - } - this._setElevation(e); - }, - - _computeKeyboardClass: function(receivedFocusFromKeyboard) { - this.toggleClass('keyboard-focus', receivedFocusFromKeyboard); - }, - - /** - * In addition to `IronButtonState` behavior, when space key goes down, - * create a ripple down effect. - * - * @param {!KeyboardEvent} event . - */ - _spaceKeyDownHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyDownHandler.call(this, event); - // Ensure that there is at most one ripple when the space key is held down. - if (this.hasRipple() && this.getRipple().ripples.length < 1) { - this._ripple.uiDownAction(); - } - }, - - /** - * In addition to `IronButtonState` behavior, when space key goes up, - * create a ripple up effect. - * - * @param {!KeyboardEvent} event . - */ - _spaceKeyUpHandler: function(event) { - Polymer.IronButtonStateImpl._spaceKeyUpHandler.call(this, event); - if (this.hasRipple()) { - this._ripple.uiUpAction(); - } - } - }; - - /** @polymerBehavior */ - Polymer.PaperButtonBehavior = [ - Polymer.IronButtonState, - Polymer.IronControlState, - Polymer.PaperRippleBehavior, - Polymer.PaperButtonBehaviorImpl - ]; \ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior.html b/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior.html deleted file mode 100644 index 966b67a2..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior.html +++ /dev/null
@@ -1,13 +0,0 @@ -<!-- -@license -Copyright (c) 2015 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---><html><head><link rel="import" href="../polymer/polymer.html"> -<link rel="import" href="../iron-behaviors/iron-button-state.html"> -<link rel="import" href="paper-ripple-behavior.html"> - -</head><body><script src="paper-button-behavior-extracted.js"></script></body></html> \ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-button/BUILD.gn b/third_party/polymer/v1_0/components-chromium/paper-button/BUILD.gn deleted file mode 100644 index e187633..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-button/BUILD.gn +++ /dev/null
@@ -1,13 +0,0 @@ -# Copyright 2019 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -# -# NOTE: Created with generate_gn.py, please do not edit. - -import("//third_party/closure_compiler/compile_js.gni") - -js_library("paper-button-extracted") { - deps = [ - "../paper-behaviors:paper-button-behavior-extracted", - ] -}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-button/bower.json b/third_party/polymer/v1_0/components-chromium/paper-button/bower.json deleted file mode 100644 index 680013d..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-button/bower.json +++ /dev/null
@@ -1,66 +0,0 @@ -{ - "name": "paper-button", - "version": "2.0.0", - "description": "Material design button", - "authors": [ - "The Polymer Authors" - ], - "keywords": [ - "web-components", - "web-component", - "polymer", - "paper", - "button" - ], - "main": "paper-button.html", - "private": true, - "repository": { - "type": "git", - "url": "git://github.com/PolymerElements/paper-button.git" - }, - "license": "http://polymer.github.io/LICENSE.txt", - "homepage": "https://github.com/PolymerElements/paper-button", - "dependencies": { - "polymer": "Polymer/polymer#1.9 - 2", - "iron-flex-layout": "PolymerElements/iron-flex-layout#1 - 2", - "paper-behaviors": "PolymerElements/paper-behaviors#1 - 2", - "paper-styles": "PolymerElements/paper-styles#1 - 2" - }, - "devDependencies": { - "iron-component-page": "PolymerElements/iron-component-page#1 - 2", - "iron-demo-helpers": "PolymerElements/iron-demo-helpers#1 - 2", - "iron-icon": "PolymerElements/iron-icon#1 - 2", - "iron-icons": "PolymerElements/iron-icons#1 - 2", - "iron-test-helpers": "PolymerElements/iron-test-helpers#1 - 2", - "test-fixture": "PolymerElements/test-fixture#^3.0.0-rc.1", - "web-component-tester": "^6.0.0", - "webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0" - }, - "variants": { - "1.x": { - "dependencies": { - "polymer": "Polymer/polymer#^1.9", - "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", - "paper-behaviors": "PolymerElements/paper-behaviors#^1.0.0", - "paper-styles": "PolymerElements/paper-styles#^1.0.0" - }, - "devDependencies": { - "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", - "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0", - "iron-icon": "PolymerElements/iron-icon#^1.0.0", - "iron-icons": "PolymerElements/iron-icons#^1.0.0", - "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0", - "test-fixture": "PolymerElements/test-fixture#^1.0.0", - "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0", - "web-component-tester": "Polymer/web-component-tester#^4.0.0" - }, - "resolutions": { - "webcomponentsjs": "^0.7" - } - } - }, - "ignore": [], - "resolutions": { - "webcomponentsjs": "^1.0.0" - } -}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-button/paper-button-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-button/paper-button-extracted.js deleted file mode 100644 index 5dddd75..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-button/paper-button-extracted.js +++ /dev/null
@@ -1,36 +0,0 @@ -Polymer({ - is: 'paper-button', - - behaviors: [ - Polymer.PaperButtonBehavior - ], - - properties: { - /** - * If true, the button should be styled with a shadow. - */ - raised: { - type: Boolean, - reflectToAttribute: true, - value: false, - observer: '_calculateElevation' - } - }, - - _calculateElevation: function() { - if (!this.raised) { - this._setElevation(0); - } else { - Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this); - } - } - - /** - Fired when the animation finishes. - This is useful if you want to wait until - the ripple animation finishes to perform some action. - - @event transitionend - Event param: {{node: Object}} detail Contains the animated node. - */ - }); \ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-button/paper-button.html b/third_party/polymer/v1_0/components-chromium/paper-button/paper-button.html deleted file mode 100644 index 65a168e92..0000000 --- a/third_party/polymer/v1_0/components-chromium/paper-button/paper-button.html +++ /dev/null
@@ -1,165 +0,0 @@ -<!-- -@license -Copyright (c) 2015 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt -The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt -The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt -Code distributed by Google as part of the polymer project is also -subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt ---><html><head><link rel="import" href="../polymer/polymer.html"> -<link rel="import" href="../iron-flex-layout/iron-flex-layout.html"> -<link rel="import" href="../paper-behaviors/paper-button-behavior.html"> -<link rel="import" href="../paper-styles/element-styles/paper-material-styles.html"> - -<!-- -Material design: [Buttons](https://www.google.com/design/spec/components/buttons.html) - -`paper-button` is a button. When the user touches the button, a ripple effect emanates -from the point of contact. It may be flat or raised. A raised button is styled with a -shadow. - -Example: - - <paper-button>Flat button</paper-button> - <paper-button raised>Raised button</paper-button> - <paper-button noink>No ripple effect</paper-button> - <paper-button toggles>Toggle-able button</paper-button> - -A button that has `toggles` true will remain `active` after being clicked (and -will have an `active` attribute set). For more information, see the `Polymer.IronButtonState` -behavior. - -You may use custom DOM in the button body to create a variety of buttons. For example, to -create a button with an icon and some text: - - <paper-button> - <iron-icon icon="favorite"></iron-icon> - custom button content - </paper-button> - -To use `paper-button` as a link, wrap it in an anchor tag. Since `paper-button` will already -receive focus, you may want to prevent the anchor tag from receiving focus as well by setting -its tabindex to -1. - - <a href="https://www.polymer-project.org/" tabindex="-1"> - <paper-button raised>Polymer Project</paper-button> - </a> - -### Styling - -Style the button with CSS as you would a normal DOM element. - - paper-button.fancy { - background: green; - color: yellow; - } - - paper-button.fancy:hover { - background: lime; - } - - paper-button[disabled], - paper-button[toggles][active] { - background: red; - } - -By default, the ripple is the same color as the foreground at 25% opacity. You may -customize the color using the `--paper-button-ink-color` custom property. - -The following custom properties and mixins are also available for styling: - -Custom property | Description | Default -----------------|-------------|---------- -`--paper-button-ink-color` | Background color of the ripple | `Based on the button's color` -`--paper-button` | Mixin applied to the button | `{}` -`--paper-button-disabled` | Mixin applied to the disabled button. Note that you can also use the `paper-button[disabled]` selector | `{}` -`--paper-button-flat-keyboard-focus` | Mixin applied to a flat button after it's been focused using the keyboard | `{}` -`--paper-button-raised-keyboard-focus` | Mixin applied to a raised button after it's been focused using the keyboard | `{}` - -@demo demo/index.html ---> - -</head><body><dom-module id="paper-button"> - <template strip-whitespace=""> - <style include="paper-material-styles"> - /* Need to specify the same specificity as the styles imported from paper-material. */ - :host { - @apply --layout-inline; - @apply --layout-center-center; - position: relative; - box-sizing: border-box; - min-width: 5.14em; - margin: 0 0.29em; - background: transparent; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-tap-highlight-color: transparent; - font: inherit; - text-transform: uppercase; - outline-width: 0; - border-radius: 3px; - user-select: none; - cursor: pointer; - z-index: 0; - padding: 0.7em 0.57em; - - @apply --paper-font-common-base; - @apply --paper-button; - } - - :host([elevation="1"]) { - @apply --paper-material-elevation-1; - } - - :host([elevation="2"]) { - @apply --paper-material-elevation-2; - } - - :host([elevation="3"]) { - @apply --paper-material-elevation-3; - } - - :host([elevation="4"]) { - @apply --paper-material-elevation-4; - } - - :host([elevation="5"]) { - @apply --paper-material-elevation-5; - } - - :host([hidden]) { - display: none !important; - } - - :host([raised].keyboard-focus) { - font-weight: bold; - @apply --paper-button-raised-keyboard-focus; - } - - :host(:not([raised]).keyboard-focus) { - font-weight: bold; - @apply --paper-button-flat-keyboard-focus; - } - - :host([disabled]) { - background: #eaeaea; - color: #a8a8a8; - cursor: auto; - pointer-events: none; - - @apply --paper-button-disabled; - } - - :host([animated]) { - @apply --shadow-transition; - } - - paper-ripple { - color: var(--paper-button-ink-color); - } - </style> - - <slot></slot> - </template> - - </dom-module> -<script src="paper-button-extracted.js"></script></body></html> \ No newline at end of file
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt index b432e63..3320e605 100644 --- a/third_party/polymer/v1_0/components_summary.txt +++ b/third_party/polymer/v1_0/components_summary.txt
@@ -172,12 +172,6 @@ Revision: ddde7f8cf41078b7a7b724473558421d6bc37f57 Tree link: https://github.com/PolymerElements/paper-behaviors/tree/v2.1.0 -Name: paper-button -Repository: https://github.com/PolymerElements/paper-button.git -Tree: v2.0.0 -Revision: b37655b85e1c3364ce9f5ca0470ddf1071cca3aa -Tree link: https://github.com/PolymerElements/paper-button/tree/v2.0.0 - Name: paper-input Repository: https://github.com/PolymerElements/paper-input.git Tree: v2.2.2
diff --git a/third_party/polymer/v1_0/rsync_exclude.txt b/third_party/polymer/v1_0/rsync_exclude.txt index ec83ac9..0299b93c 100644 --- a/third_party/polymer/v1_0/rsync_exclude.txt +++ b/third_party/polymer/v1_0/rsync_exclude.txt
@@ -46,6 +46,7 @@ iron-form-element-behavior/* # paper-behaviors +paper-behaviors/paper-button-behavior.html paper-behaviors/paper-checked-element-behavior.html paper-behaviors/paper-inky-focus-behavior.html
diff --git a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js b/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js deleted file mode 100644 index 158a366..0000000 --- a/third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js +++ /dev/null
@@ -1,90 +0,0 @@ -/** -@license -Copyright (c) 2015 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at -http://polymer.github.io/LICENSE.txt The complete set of authors may be found at -http://polymer.github.io/AUTHORS.txt The complete set of contributors may be -found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as -part of the polymer project is also subject to an additional IP rights grant -found at http://polymer.github.io/PATENTS.txt -*/ -import '../polymer/polymer_bundled.min.js'; - -import {IronButtonState, IronButtonStateImpl} from '../iron-behaviors/iron-button-state.js'; -import {IronControlState} from '../iron-behaviors/iron-control-state.js'; - -import {PaperRippleBehavior} from './paper-ripple-behavior.js'; - -/** @polymerBehavior PaperButtonBehavior */ -export const PaperButtonBehaviorImpl = { - properties: { - /** - * The z-depth of this element, from 0-5. Setting to 0 will remove the - * shadow, and each increasing number greater than 0 will be "deeper" - * than the last. - * - * @attribute elevation - * @type number - * @default 1 - */ - elevation: {type: Number, reflectToAttribute: true, readOnly: true} - }, - - observers: [ - '_calculateElevation(focused, disabled, active, pressed, receivedFocusFromKeyboard)', - '_computeKeyboardClass(receivedFocusFromKeyboard)' - ], - - hostAttributes: {role: 'button', tabindex: '0', animated: true}, - - _calculateElevation: function() { - var e = 1; - if (this.disabled) { - e = 0; - } else if (this.active || this.pressed) { - e = 4; - } else if (this.receivedFocusFromKeyboard) { - e = 3; - } - this._setElevation(e); - }, - - _computeKeyboardClass: function(receivedFocusFromKeyboard) { - this.toggleClass('keyboard-focus', receivedFocusFromKeyboard); - }, - - /** - * In addition to `IronButtonState` behavior, when space key goes down, - * create a ripple down effect. - * - * @param {!KeyboardEvent} event . - */ - _spaceKeyDownHandler: function(event) { - IronButtonStateImpl._spaceKeyDownHandler.call(this, event); - // Ensure that there is at most one ripple when the space key is held down. - if (this.hasRipple() && this.getRipple().ripples.length < 1) { - this._ripple.uiDownAction(); - } - }, - - /** - * In addition to `IronButtonState` behavior, when space key goes up, - * create a ripple up effect. - * - * @param {!KeyboardEvent} event . - */ - _spaceKeyUpHandler: function(event) { - IronButtonStateImpl._spaceKeyUpHandler.call(this, event); - if (this.hasRipple()) { - this._ripple.uiUpAction(); - } - } -}; - -/** @polymerBehavior */ -export const PaperButtonBehavior = [ - IronButtonState, - IronControlState, - PaperRippleBehavior, - PaperButtonBehaviorImpl -];
diff --git a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js b/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js deleted file mode 100644 index 42dc7218..0000000 --- a/third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js +++ /dev/null
@@ -1,211 +0,0 @@ -/** -@license -Copyright (c) 2015 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at -http://polymer.github.io/LICENSE.txt The complete set of authors may be found at -http://polymer.github.io/AUTHORS.txt The complete set of contributors may be -found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as -part of the polymer project is also subject to an additional IP rights grant -found at http://polymer.github.io/PATENTS.txt -*/ -import '../iron-flex-layout/iron-flex-layout.js'; -import '../paper-styles/element-styles/paper-material-styles.js'; - -import {PaperButtonBehavior, PaperButtonBehaviorImpl} from '../paper-behaviors/paper-button-behavior.js'; -import {Polymer} from '../polymer/polymer_bundled.min.js'; -import {html} from '../polymer/polymer_bundled.min.js'; - -const template = html` - <style include="paper-material-styles"> - /* Need to specify the same specificity as the styles imported from paper-material. */ - :host { - @apply --layout-inline; - @apply --layout-center-center; - position: relative; - box-sizing: border-box; - min-width: 5.14em; - margin: 0 0.29em; - background: transparent; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-tap-highlight-color: transparent; - font: inherit; - text-transform: uppercase; - outline-width: 0; - border-radius: 3px; - -moz-user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - user-select: none; - cursor: pointer; - z-index: 0; - padding: 0.7em 0.57em; - - @apply --paper-font-common-base; - @apply --paper-button; - } - - :host([elevation="1"]) { - @apply --paper-material-elevation-1; - } - - :host([elevation="2"]) { - @apply --paper-material-elevation-2; - } - - :host([elevation="3"]) { - @apply --paper-material-elevation-3; - } - - :host([elevation="4"]) { - @apply --paper-material-elevation-4; - } - - :host([elevation="5"]) { - @apply --paper-material-elevation-5; - } - - :host([hidden]) { - display: none !important; - } - - :host([raised].keyboard-focus) { - font-weight: bold; - @apply --paper-button-raised-keyboard-focus; - } - - :host(:not([raised]).keyboard-focus) { - font-weight: bold; - @apply --paper-button-flat-keyboard-focus; - } - - :host([disabled]) { - background: none; - color: #a8a8a8; - cursor: auto; - pointer-events: none; - - @apply --paper-button-disabled; - } - - :host([disabled][raised]) { - background: #eaeaea; - } - - - :host([animated]) { - @apply --shadow-transition; - } - - paper-ripple { - color: var(--paper-button-ink-color); - } - </style> - - <slot></slot>`; - -template.setAttribute('strip-whitespace', ''); - -/** -Material design: -[Buttons](https://www.google.com/design/spec/components/buttons.html) - -`paper-button` is a button. When the user touches the button, a ripple effect -emanates from the point of contact. It may be flat or raised. A raised button is -styled with a shadow. - -Example: - - <paper-button>Flat button</paper-button> - <paper-button raised>Raised button</paper-button> - <paper-button noink>No ripple effect</paper-button> - <paper-button toggles>Toggle-able button</paper-button> - -A button that has `toggles` true will remain `active` after being clicked (and -will have an `active` attribute set). For more information, see the -`IronButtonState` behavior. - -You may use custom DOM in the button body to create a variety of buttons. For -example, to create a button with an icon and some text: - - <paper-button> - <iron-icon icon="favorite"></iron-icon> - custom button content - </paper-button> - -To use `paper-button` as a link, wrap it in an anchor tag. Since `paper-button` -will already receive focus, you may want to prevent the anchor tag from -receiving focus as well by setting its tabindex to -1. - - <a href="https://www.polymer-project.org/" tabindex="-1"> - <paper-button raised>Polymer Project</paper-button> - </a> - -### Styling - -Style the button with CSS as you would a normal DOM element. - - paper-button.fancy { - background: green; - color: yellow; - } - - paper-button.fancy:hover { - background: lime; - } - - paper-button[disabled], - paper-button[toggles][active] { - background: red; - } - -By default, the ripple is the same color as the foreground at 25% opacity. You -may customize the color using the `--paper-button-ink-color` custom property. - -The following custom properties and mixins are also available for styling: - -Custom property | Description | Default -----------------|-------------|---------- -`--paper-button-ink-color` | Background color of the ripple | `Based on the button's color` -`--paper-button` | Mixin applied to the button | `{}` -`--paper-button-disabled` | Mixin applied to the disabled button. Note that you can also use the `paper-button[disabled]` selector | `{}` -`--paper-button-flat-keyboard-focus` | Mixin applied to a flat button after it's been focused using the keyboard | `{}` -`--paper-button-raised-keyboard-focus` | Mixin applied to a raised button after it's been focused using the keyboard | `{}` - -@demo demo/index.html -*/ -Polymer({ - _template: template, - - is: 'paper-button', - - behaviors: [PaperButtonBehavior], - - properties: { - /** - * If true, the button should be styled with a shadow. - */ - raised: { - type: Boolean, - reflectToAttribute: true, - value: false, - observer: '_calculateElevation', - } - }, - - _calculateElevation: function() { - if (!this.raised) { - this._setElevation(0); - } else { - PaperButtonBehaviorImpl._calculateElevation.apply(this); - } - } - - /** - Fired when the animation finishes. - This is useful if you want to wait until - the ripple animation finishes to perform some action. - - @event transitionend - Event param: {{node: Object}} detail Contains the animated node. - */ -});
diff --git a/third_party/polymer/v3_0/package-lock.json b/third_party/polymer/v3_0/package-lock.json index 102529e..3edb5c33 100644 --- a/third_party/polymer/v3_0/package-lock.json +++ b/third_party/polymer/v3_0/package-lock.json
@@ -14,7 +14,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-a11y-announcer/-/iron-a11y-announcer-3.0.2.tgz", "integrity": "sha512-LqnMF39mXyxSSRbTHRzGbcJS8nU0NVTo2raBOgOlpxw5yfGJUVcwaTJ/qy5NtWCZLRfa4suycf0oAkuUjHTXHQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-a11y-keys": { @@ -22,8 +22,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-a11y-keys/-/iron-a11y-keys-3.0.1.tgz", "integrity": "sha512-zmTi8xHeY4ZMJLAitW2hAmW5zXZ35hVy/eHQUFadAlOccuBK3oRRmoPRQRaZgpyJrCVFDAQRXXzzJtUDil/0CA==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-a11y-keys-behavior": { @@ -31,7 +31,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-a11y-keys-behavior/-/iron-a11y-keys-behavior-3.0.1.tgz", "integrity": "sha512-lnrjKq3ysbBPT/74l0Fj0U9H9C35Tpw2C/tpJ8a+5g8Y3YJs1WSZYnEl1yOkw6sEyaxOq/1DkzH0+60gGu5/PQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-autogrow-textarea": { @@ -39,10 +39,10 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-autogrow-textarea/-/iron-autogrow-textarea-3.0.1.tgz", "integrity": "sha512-FgSL7APrOSL9Vu812sBCFlQ17hvnJsBAV2C2e1UAiaHhB+dyfLq8gGdGUpqVWuGJ50q4Y/49QwCNnLf85AdVYA==", "requires": { - "@polymer/iron-behaviors": "3.0.1", - "@polymer/iron-flex-layout": "3.0.1", - "@polymer/iron-validatable-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-behaviors": "^3.0.0-pre.26", + "@polymer/iron-flex-layout": "^3.0.0-pre.26", + "@polymer/iron-validatable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-behaviors": { @@ -50,8 +50,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-behaviors/-/iron-behaviors-3.0.1.tgz", "integrity": "sha512-IMEwcv1lhf1HSQxuyWOUIL0lOBwmeaoSTpgCJeP9IBYnuB1SPQngmfRuHKgK6/m9LQ9F9miC7p3HeQQUdKAE0w==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-checked-element-behavior": { @@ -59,9 +59,9 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-checked-element-behavior/-/iron-checked-element-behavior-3.0.1.tgz", "integrity": "sha512-aDr0cbCNVq49q+pOqa6CZutFh+wWpwPMLpEth9swx+GkAj+gCURhuQkaUYhIo5f2egDbEioR1aeHMnPlU9dQZA==", "requires": { - "@polymer/iron-form-element-behavior": "3.0.1", - "@polymer/iron-validatable-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-form-element-behavior": "^3.0.0-pre.26", + "@polymer/iron-validatable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-collapse": { @@ -69,8 +69,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-collapse/-/iron-collapse-3.0.1.tgz", "integrity": "sha512-yg6q5ZyckQR9VL9VmLrSTkSFXWy9AcJC8KtnD5cg0EHRPbakE8I9S/gVAgeP4nMWV2a/BjLLC4IBygcCMDhAGw==", "requires": { - "@polymer/iron-resizable-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-resizable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-dropdown": { @@ -78,10 +78,10 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-dropdown/-/iron-dropdown-3.0.1.tgz", "integrity": "sha512-22yLhepfcKjuQMfFmRHi/9MPKTqkzgRrmWWW0P5uqK++xle53k2QBO5VYUAYiCN3ZcxIi9lEhZ9YWGeQj2JBig==", "requires": { - "@polymer/iron-behaviors": "3.0.1", - "@polymer/iron-overlay-behavior": "3.0.2", - "@polymer/neon-animation": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-behaviors": "^3.0.0-pre.26", + "@polymer/iron-overlay-behavior": "^3.0.0-pre.27", + "@polymer/neon-animation": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-fit-behavior": { @@ -89,7 +89,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-fit-behavior/-/iron-fit-behavior-3.0.1.tgz", "integrity": "sha512-/M0B1L30k31vmwNBaGuZcxzUAhJSHoGccb/DF0CDKI/hT8UlkTvcyemaWdOpmHHLgY52ceKIkRwA3AeXrKyvaQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-flex-layout": { @@ -97,7 +97,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-flex-layout/-/iron-flex-layout-3.0.1.tgz", "integrity": "sha512-7gB869czArF+HZcPTVSgvA7tXYFze9EKckvM95NB7SqYF+NnsQyhoXgKnpFwGyo95lUjUW9TFDLUwDXnCYFtkw==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-form-element-behavior": { @@ -105,7 +105,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-form-element-behavior/-/iron-form-element-behavior-3.0.1.tgz", "integrity": "sha512-G/e2KXyL5AY7mMjmomHkGpgS0uAf4ovNpKhkuUTRnMuMJuf589bKqE85KN4ovE1Tzhv2hJoh/igyD6ekHiYU1A==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-icon": { @@ -113,9 +113,9 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-icon/-/iron-icon-3.0.1.tgz", "integrity": "sha512-QLPwirk+UPZNaLnMew9VludXA4CWUCenRewgEcGYwdzVgDPCDbXxy6vRJjmweZobMQv/oVLppT2JZtJFnPxX6g==", "requires": { - "@polymer/iron-flex-layout": "3.0.1", - "@polymer/iron-meta": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-flex-layout": "^3.0.0-pre.26", + "@polymer/iron-meta": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-iconset-svg": { @@ -123,8 +123,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-iconset-svg/-/iron-iconset-svg-3.0.1.tgz", "integrity": "sha512-XNwURbNHRw6u2fJe05O5fMYye6GSgDlDqCO+q6K1zAnKIrpgZwf2vTkBd5uCcZwsN0FyCB3mvNZx4jkh85dRDw==", "requires": { - "@polymer/iron-meta": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-meta": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-input": { @@ -132,9 +132,9 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-input/-/iron-input-3.0.1.tgz", "integrity": "sha512-WLx13kEcbH9GKbj9+pWR6pbJkA5kxn3796ynx6eQd2rueMyUfVTR3GzOvadBKsciUuIuzrxpBWZ2+3UcueVUQQ==", "requires": { - "@polymer/iron-a11y-announcer": "3.0.2", - "@polymer/iron-validatable-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-announcer": "^3.0.0-pre.26", + "@polymer/iron-validatable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-list": { @@ -142,10 +142,10 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-list/-/iron-list-3.0.2.tgz", "integrity": "sha512-A6GMTx/4mWxGn3BC6weaXqRC7B0coNjgEoxwVbEcDNwovqntv0aWs5sT9+PI4zvV/syWL5rAESyJDvOxTatoEw==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/iron-resizable-behavior": "3.0.1", - "@polymer/iron-scroll-target-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/iron-resizable-behavior": "^3.0.0-pre.26", + "@polymer/iron-scroll-target-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-location": { @@ -153,7 +153,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-location/-/iron-location-3.0.1.tgz", "integrity": "sha512-almb+p/fdSi4bxG+vyXjY51fDZxHMxwiug51Lfvr86wZRXN/u21Y6BapxG5n9f0hPSy9fimjIAvaYmozi7VjyQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-media-query": { @@ -161,7 +161,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-media-query/-/iron-media-query-3.0.1.tgz", "integrity": "sha512-czUX1pm1zfmfcZtq5J57XFkcobBv08Y50exp0/3v8Bos5VL/jv2tU0RwiTfDBxUMhjicGbgwEBFQPY2V5DMzyw==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-meta": { @@ -169,7 +169,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-meta/-/iron-meta-3.0.1.tgz", "integrity": "sha512-pWguPugiLYmWFV9UWxLWzZ6gm4wBwQdDy4VULKwdHCqR7OP7u98h+XDdGZsSlDPv6qoryV/e3tGHlTIT0mbzJA==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-overlay-behavior": { @@ -177,10 +177,10 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-overlay-behavior/-/iron-overlay-behavior-3.0.2.tgz", "integrity": "sha512-j1qmt6mJHCwpe1mKOvqK5kcCUPQr5LSrlqpgRDbUuLgUfNJ/vGTipjrkBlfbEUagm5FEQdc1VLPLSQP6WVuP9g==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/iron-fit-behavior": "3.0.1", - "@polymer/iron-resizable-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/iron-fit-behavior": "^3.0.0-pre.26", + "@polymer/iron-resizable-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-pages": { @@ -188,9 +188,9 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-pages/-/iron-pages-3.0.1.tgz", "integrity": "sha512-PQe8S1JKHPcsIvFOaQP+9+AXmqUIL9fPqC6xT63OAZQxYCeZJDKgT9GKBx+VRryYBUlj2FLEXkUVpG+PTotdjg==", "requires": { - "@polymer/iron-resizable-behavior": "3.0.1", - "@polymer/iron-selector": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-resizable-behavior": "^3.0.0-pre.26", + "@polymer/iron-selector": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-range-behavior": { @@ -198,7 +198,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-range-behavior/-/iron-range-behavior-3.0.1.tgz", "integrity": "sha512-+jtL9v45M/T1RJleWyQaNH84S9/mIIR+AjNbYIttbKGp1eG+98j8MDWe7LXNtg79V2LQnE/+VS82cBeELyGVeg==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-resizable-behavior": { @@ -206,7 +206,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-resizable-behavior/-/iron-resizable-behavior-3.0.1.tgz", "integrity": "sha512-FyHxRxFspVoRaeZSWpT3y0C9awomb4tXXolIJcZ7RvXhMP632V5lez+ch5G5SwK0LpnAPkg35eB0LPMFv+YMMQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-scroll-target-behavior": { @@ -214,7 +214,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-scroll-target-behavior/-/iron-scroll-target-behavior-3.0.1.tgz", "integrity": "sha512-xg1WanG25BIkQE8rhuReqY9zx1K5M7F+YAIYpswEp5eyDIaZ1Y3vUmVeQ3KG+hiSugzI1M752azXN7kvyhOBcQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-scroll-threshold": { @@ -222,8 +222,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-scroll-threshold/-/iron-scroll-threshold-3.0.1.tgz", "integrity": "sha512-Zm06AP1CDfa19b5yTvLRjBfDA85zMh4XunL2/Fz5n2faZcP5zr5tIBtXsAqz4ug9I3Uau7rjzkyL7Cc0ni8sUA==", "requires": { - "@polymer/iron-scroll-target-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-scroll-target-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-selector": { @@ -231,7 +231,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-selector/-/iron-selector-3.0.1.tgz", "integrity": "sha512-sBVk2uas6prW0glUe2xEJJYlvxmYzM40Au9OKbfDK2Qekou/fLKcBRyIYI39kuI8zWRaip8f3CI8qXcUHnKb1A==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-test-helpers": { @@ -239,7 +239,7 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-test-helpers/-/iron-test-helpers-3.0.1.tgz", "integrity": "sha512-2R7dnGcW2eg95i7LhYWWUO4AlAk6qXsPnKoyeN2R1t0km0ECMx0jjwqeLwCo8/7LwuVPZSiarI4DK7jyU7fJLQ==", "requires": { - "@polymer/polymer": "3.2.0" + "@polymer/polymer": "^3.0.0" } }, "@polymer/iron-validatable-behavior": { @@ -247,8 +247,8 @@ "resolved": "https://registry.npmjs.org/@polymer/iron-validatable-behavior/-/iron-validatable-behavior-3.0.1.tgz", "integrity": "sha512-wwpYh6wOa4fNI+jH5EYKC7TVPYQ2OfgQqocWat7GsNWcsblKYhLYbwsvEY5nO0n2xKqNfZzDLrUom5INJN7msQ==", "requires": { - "@polymer/iron-meta": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-meta": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/neon-animation": { @@ -256,9 +256,9 @@ "resolved": "https://registry.npmjs.org/@polymer/neon-animation/-/neon-animation-3.0.1.tgz", "integrity": "sha512-cDDc0llpVCe0ATbDS3clDthI54Bc8YwZIeTGGmBJleKOvbRTUC5+ssJmRL+VwVh+VM5FlnQlx760ppftY3uprg==", "requires": { - "@polymer/iron-resizable-behavior": "3.0.1", - "@polymer/iron-selector": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-resizable-behavior": "^3.0.0-pre.26", + "@polymer/iron-selector": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-behaviors": { @@ -266,21 +266,10 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-behaviors/-/paper-behaviors-3.0.1.tgz", "integrity": "sha512-6knhj69fPJejv8qR0kCSUY+Q0XjaUf0OSnkjRjmTJPAwSrRYtgqE+l6P1FfA+py1X/cUjgne9EF5rMZAKJIg1g==", "requires": { - "@polymer/iron-behaviors": "3.0.1", - "@polymer/iron-checked-element-behavior": "3.0.1", - "@polymer/paper-ripple": "3.0.1", - "@polymer/polymer": "3.2.0" - } - }, - "@polymer/paper-button": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@polymer/paper-button/-/paper-button-3.0.1.tgz", - "integrity": "sha512-JRNBc+Oj9EWnmyLr7FcCr8T1KAnEHPh6mosln9BUdkM+qYaYsudSICh3cjTIbnj6AuF5OJidoLkM1dlyj0j6Zg==", - "requires": { - "@polymer/iron-flex-layout": "3.0.1", - "@polymer/paper-behaviors": "3.0.1", - "@polymer/paper-styles": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-behaviors": "^3.0.0-pre.26", + "@polymer/iron-checked-element-behavior": "^3.0.0-pre.26", + "@polymer/paper-ripple": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-input": { @@ -288,13 +277,13 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-input/-/paper-input-3.0.2.tgz", "integrity": "sha512-EoyJLsUCo7zLQp63jG7+qbRcN7ynT0p9MktDeH+dnl29UqFD4Ovj2/O5cSgq3lA3dYrei4vHF11Qmdmk7iab7Q==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/iron-autogrow-textarea": "3.0.1", - "@polymer/iron-behaviors": "3.0.1", - "@polymer/iron-form-element-behavior": "3.0.1", - "@polymer/iron-input": "3.0.1", - "@polymer/paper-styles": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/iron-autogrow-textarea": "^3.0.0-pre.26", + "@polymer/iron-behaviors": "^3.0.0-pre.26", + "@polymer/iron-form-element-behavior": "^3.0.0-pre.26", + "@polymer/iron-input": "^3.0.0-pre.26", + "@polymer/paper-styles": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-progress": { @@ -302,10 +291,10 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-progress/-/paper-progress-3.0.1.tgz", "integrity": "sha512-5nguG+tmnyoaWKVNG8Smtno2uLSPBgEsT3f20JY8yJTjUBYWaqa8E3l5RLkTRXgA4x9OnvLb8/CdlQWXQIogBg==", "requires": { - "@polymer/iron-flex-layout": "3.0.1", - "@polymer/iron-range-behavior": "3.0.1", - "@polymer/paper-styles": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-flex-layout": "^3.0.0-pre.26", + "@polymer/iron-range-behavior": "^3.0.0-pre.26", + "@polymer/paper-styles": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-ripple": { @@ -313,8 +302,8 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-ripple/-/paper-ripple-3.0.1.tgz", "integrity": "sha512-dgOe12GyCF1VZBLUQqnzGWlf3xb255FajNCVB1VFj/AtskYtoamnafa7m3a+1vs+C8qbg4Benn5KwgxVDSW4cg==", "requires": { - "@polymer/iron-a11y-keys-behavior": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/iron-a11y-keys-behavior": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-spinner": { @@ -322,8 +311,8 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-spinner/-/paper-spinner-3.0.2.tgz", "integrity": "sha512-XUzu8/4NH+pnNZUTI2MxtOKFAr0EOsW7eGhTg3VBhTh7DDW/q3ewzwYRWnqNJokX9BEnxKMiXXaIeTEBq4k2dw==", "requires": { - "@polymer/paper-styles": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/paper-styles": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-styles": { @@ -331,9 +320,9 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-styles/-/paper-styles-3.0.1.tgz", "integrity": "sha512-y6hmObLqlCx602TQiSBKHqjwkE7xmDiFkoxdYGaNjtv4xcysOTdVJsDR/R9UHwIaxJ7gHlthMSykir1nv78++g==", "requires": { - "@polymer/font-roboto": "3.0.2", - "@polymer/iron-flex-layout": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/font-roboto": "^3.0.1", + "@polymer/iron-flex-layout": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/paper-tooltip": { @@ -341,8 +330,8 @@ "resolved": "https://registry.npmjs.org/@polymer/paper-tooltip/-/paper-tooltip-3.0.1.tgz", "integrity": "sha512-yiUk09opTEnE1lK+tb501ENb+yQBi4p++Ep0eGJAHesVYKVMPNgPphVKkIizkDaU+n0SE+zXfTsRbYyOMDYXSg==", "requires": { - "@polymer/paper-styles": "3.0.1", - "@polymer/polymer": "3.2.0" + "@polymer/paper-styles": "^3.0.0-pre.26", + "@polymer/polymer": "^3.0.0" } }, "@polymer/polymer": { @@ -350,7 +339,7 @@ "resolved": "https://registry.npmjs.org/@polymer/polymer/-/polymer-3.2.0.tgz", "integrity": "sha512-L6uV1oM6T6xbwbVx6t3biG5T2VSSB03LxnIrUd9M2pr6RkHVPFHJ37pC5MUwBAEhkGFJif7eks7fdMMSGZTeEQ==", "requires": { - "@webcomponents/shadycss": "1.9.1" + "@webcomponents/shadycss": "^1.8.0" } }, "@types/estree": { @@ -379,8 +368,8 @@ "integrity": "sha512-sHg0F05oTMJzM592MWU8irsPx8LIFMKSCnEkcp6vp/gnj+oJ9GJEBW9hl8jUqy2L6Q2uUxFzPgvoExLbfuSODA==", "requires": { "@types/estree": "0.0.39", - "@types/node": "12.0.3", - "acorn": "6.1.1" + "@types/node": "^12.0.2", + "acorn": "^6.1.1" } } }
diff --git a/third_party/polymer/v3_0/package.json b/third_party/polymer/v3_0/package.json index 866685a..73bbc37 100644 --- a/third_party/polymer/v3_0/package.json +++ b/third_party/polymer/v3_0/package.json
@@ -30,7 +30,6 @@ "@polymer/iron-validatable-behavior": "3.0.1", "@polymer/neon-animation": "3.0.1", "@polymer/paper-behaviors": "3.0.1", - "@polymer/paper-button": "3.0.1", "@polymer/paper-input": "3.0.2", "@polymer/paper-progress": "3.0.1", "@polymer/paper-ripple": "3.0.1",
diff --git a/third_party/polymer/v3_0/rsync_exclude.txt b/third_party/polymer/v3_0/rsync_exclude.txt index 1b00b5c..cda16318 100644 --- a/third_party/polymer/v3_0/rsync_exclude.txt +++ b/third_party/polymer/v3_0/rsync_exclude.txt
@@ -41,6 +41,7 @@ iron-form-element-behavior/* # paper-behaviors +paper-behaviors/paper-button-behavior.js paper-behaviors/paper-checked-element-behavior.js # paper-input
diff --git a/tools/android/native_lib_memory/OWNERS b/tools/android/native_lib_memory/OWNERS index d45b803a..3301555 100644 --- a/tools/android/native_lib_memory/OWNERS +++ b/tools/android/native_lib_memory/OWNERS
@@ -1,3 +1,2 @@ lizeb@chromium.org -mattcary@chromium.org pasko@chromium.org
diff --git a/tools/binary_size/find_large_commits.py b/tools/binary_size/find_large_commits.py new file mode 100755 index 0000000..f9eac8ae --- /dev/null +++ b/tools/binary_size/find_large_commits.py
@@ -0,0 +1,93 @@ +#!/usr/bin/env python +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Prints the large commits given a .csv file from a telemetry size graph.""" + +import argparse +import re +import subprocess + + +def _ReadCsv(path): + """Returns the contents of the .csv as a list of (int, int).""" + ret = [] + with open(path) as f: + for line in f: + parts = line.rstrip().split(',') + if len(parts) == 2 and parts[0] != 'revision': + ret.append((int(parts[0]), int(float(parts[1])))) + return ret + + +def _FindBigDeltas(revs_and_sizes, increase_threshold, decrease_threshold): + """Filters revs_and_sizes for entries that grow/shrink too much.""" + big_jumps = [] + prev_size = revs_and_sizes[0][1] + for rev, size in revs_and_sizes: + delta = size - prev_size + prev_size = size + if delta > increase_threshold or -delta > decrease_threshold: + big_jumps.append((rev, delta)) + return big_jumps + + +def _LookupCommitInfo(rev): + sha1 = subprocess.check_output(['git', 'crrev-parse', str(rev)]).strip() + desc = subprocess.check_output(['git', 'log', '-n1', sha1]) + author = re.search(r'Author: .*?<(.*?)>', desc).group(1) + day, year = re.search(r'Date:\s+\w+\s+(\w+ \d+)\s+.*?\s+(\d+)', desc).groups() + date = '{} {}'.format(day, year) + title = re.search(r'\n +(\S.*)', desc).group(1).replace('\t', ' ') + milestone = None + if 'Roll AFDO' not in title: + releases = subprocess.check_output(['git', 'find-releases', sha1]) + version = re.search('initially in (\d\d)', releases) + milestone = '' + if version: + milestone = 'M{}'.format(version.group(1)) + version = re.search('initially in branch-heads/(\d\d\d\d)', releases) + if version: + milestone = version.group(1) + + return sha1, author, date, title, milestone + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--increase-threshold', + type=int, + default=50 * 1024, + help='Minimum number of bytes larger to be considered a notable.') + parser.add_argument( + '--decrease-threshold', + type=int, + default=30 * 1024, + help='Minimum number of bytes smaller to be considered a notable.') + parser.add_argument( + 'points_csv', help='Input .csv file with columns: revision,value') + options = parser.parse_args() + + revs_and_sizes = _ReadCsv(options.points_csv) + rev_and_delta = _FindBigDeltas(revs_and_sizes, options.increase_threshold, + options.decrease_threshold) + + print 'Printing info for up to {} commits in the range {}-{}'.format( + len(rev_and_delta), revs_and_sizes[0][0], revs_and_sizes[-1][0]) + print 'Revision,Hash,Title,Author,Delta,Date,Milestone' + afdo_count = 0 + for rev, delta in rev_and_delta: + sha1, author, date, title, milestone = _LookupCommitInfo(rev) + if milestone is not None: + print '\t'.join( + [str(rev), sha1, title, author, + str(delta), date, milestone]) + else: + afdo_count += 1 + print 'Skipped %d AFDO rolls' % afdo_count + + +if __name__ == '__main__': + main()
diff --git a/tools/cygprofile/OWNERS b/tools/cygprofile/OWNERS index d45b803a..3301555 100644 --- a/tools/cygprofile/OWNERS +++ b/tools/cygprofile/OWNERS
@@ -1,3 +1,2 @@ lizeb@chromium.org -mattcary@chromium.org pasko@chromium.org
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 907dd0e..9b0e724 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -123,7 +123,7 @@ 'ToTAndroid x64': 'android_clang_tot_x64', 'ToTLinuxOfficial': 'clang_tot_official', 'ToTLinux': 'clang_tot_linux_full_symbols_shared_release', - 'ToTLinuxCoverage': 'clang_tot_coverage_minimal_symbols_shared_release_with_libfuzzer', + 'ToTLinuxCoverage': 'clang_tot_coverage_minimal_symbols_release_with_libfuzzer', 'ToTLinux (dbg)': 'clang_tot_shared_debug', 'ToTLinuxASan': 'clang_tot_asan_lsan_static_release', 'ToTLinuxASanLibfuzzer': 'libfuzzer_asan_clang_tot_release', @@ -133,7 +133,7 @@ 'ToTLinuxUBSanVptr': 'clang_tot_ubsan_no_recover_hack_static_release', 'ToTMac': 'clang_tot_minimal_symbols_shared_release', 'ToTMacOfficial': 'clang_tot_official', - 'ToTMacCoverage': 'clang_tot_coverage_minimal_symbols_shared_release', + 'ToTMacCoverage': 'clang_tot_coverage_minimal_symbols_release', 'ToTMac (dbg)': 'clang_tot_shared_debug', 'ToTMacASan': 'asan_disable_nacl_clang_tot_minimal_symbols_static_release', 'ToTWin': 'clang_tot_official_minimal_symbols_static_release_x86', @@ -1276,12 +1276,12 @@ 'clang_tot', 'minimal_symbols', 'shared', 'release', ], - 'clang_tot_coverage_minimal_symbols_shared_release_with_libfuzzer': [ - 'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'shared', 'release', 'libfuzzer', + 'clang_tot_coverage_minimal_symbols_release_with_libfuzzer': [ + 'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'release', 'libfuzzer', ], - 'clang_tot_coverage_minimal_symbols_shared_release': [ - 'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'shared', 'release', + 'clang_tot_coverage_minimal_symbols_release': [ + 'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'release', ], 'clang_tot_shared_release_dcheck': [
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 9d85e7d..a41dee6 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -34199,6 +34199,7 @@ <int value="-1234740672" label="UsePdfCompositorServiceForPrint:disabled"/> <int value="-1232629319" label="ResamplingScrollEvents:disabled"/> <int value="-1230610048" label="SyncWifiConfigurations:disabled"/> + <int value="-1228035246" label="PasswordEditingAndroid:enabled"/> <int value="-1227660915" label="WebContentsOcclusion:disabled"/> <int value="-1225629234" label="SyncPseudoUSSFavicons:enabled"/> <int value="-1225198073" label="ReaderModeInCCT:disabled"/> @@ -35155,6 +35156,7 @@ <int value="149914698" label="SearchReadyOmnibox:disabled"/> <int value="151022756" label="ArcAvailableForChildAccount:disabled"/> <int value="151101719" label="HtmlBaseUsernameDetector:enabled"/> + <int value="152867897" label="PasswordEditingAndroid:disabled"/> <int value="153347646" label="SmartDimModelV3:disabled"/> <int value="155977192" label="EnableFileManagerFormatDialog:disabled"/> <int value="157217034" label="enable-tab-for-desktop-share"/> @@ -37867,6 +37869,8 @@ <int value="638" label="overflow-inline"/> <int value="639" label="overflow-block"/> <int value="640" label="forced-color-adjust"/> + <int value="641" label="overscroll-behavior-inline"/> + <int value="642" label="overscroll-behavior-block"/> </enum> <enum name="MappedEditingCommands"> @@ -44911,6 +44915,12 @@ <int value="15" label="Generated password deleted by autofilling"/> </enum> +<enum name="PasswordGenerationPresaveConflict"> + <int value="0" label="No username conflict"/> + <int value="1" label="No conflict with empty username"/> + <int value="2" label="Conflict with empty username"/> +</enum> + <enum name="PasswordGenerationUserEvent"> <int value="0" label="Password accepted"/> <int value="1" label="Password edited"/> @@ -59316,13 +59326,17 @@ <int value="2" label="Failed - Parse Error"/> <int value="3" label="Failed - Signature Mismatch"/> <int value="4" label="Failed - Gzip Compress Error"/> - <int value="5" label="Delta Count"/> + <int value="5" label="Delta Count (obsolete)"/> <int value="6" label="Failed - Delta: Read Seed"/> <int value="7" label="Failed - Delta: Apply"/> <int value="8" label="Failed - Delta: Store Seed"/> <int value="9" label="Failed - Gzip Uncompress Error"/> <int value="10" label="Failed - Empty Gzip Contents"/> <int value="11" label="Failed - Unsupported Seed Format"/> + <int value="12" label="Gzip Delta Count"/> + <int value="13" label="Non-Gzip Delta Count"/> + <int value="14" label="Gzip Full Count"/> + <int value="15" label="Non-Gzip Full Count"/> </enum> <enum name="VaryType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 974a0c9..842d6ccc2 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -3618,6 +3618,23 @@ </summary> </histogram> +<histogram + name="Android.WebView.ShouldInterceptRequest.NullInputStream.ResponseStatusCode" + enum="HttpResponseCode" expires_after="2020-06-14"> + <owner>timvolodine@chromium.org</owner> + <owner>tobiasjs@chromium.org</owner> + <owner>ntfschr@chromium.org</owner> + <summary> + Records the custom response status code for the intercepted requests where + input stream is null. In case status code is invalid (or has not been + specified by the app) a zero status code is recorded. This UMA is needed in + order to track specific usages of request interception where the behavior + with network service enabled is different from the old code path (for more + details see go/wv-ns-behavior-differences). This data is recorded regardless + of whether the network service is enabled or disabled. + </summary> +</histogram> + <histogram name="Android.WebView.SplitApkWorkaroundResult" enum="SplitApkWorkaroundResult" expires_after="M72"> <owner>tiborg@chromium.org</owner> @@ -14587,6 +14604,10 @@ </histogram> <histogram name="BlinkGC.AtomicPhaseMarking" units="ms"> + <obsolete> + See BlinkGC.TimeForAtomicPhaseMarking. + </obsolete> + <owner>mlippautz@chromium.org</owner> <owner>oilpan-reviews@chromium.org</owner> <summary> Duration of finishing marking the transitive closure of objects during the @@ -14662,15 +14683,19 @@ </summary> </histogram> -<histogram name="BlinkGC.ObjectSizeAfterGC" units="KB"> +<histogram name="BlinkGC.ObjectSizeAfterGC" units="KB" expires_after="M80"> <owner>haraken@chromium.org</owner> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> <summary> The size of allocated objects just after Blink GC is triggered. </summary> </histogram> -<histogram name="BlinkGC.ObjectSizeBeforeGC" units="KB" expires_after="M77"> +<histogram name="BlinkGC.ObjectSizeBeforeGC" units="KB" expires_after="M80"> <owner>haraken@chromium.org</owner> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> <summary> The size of allocated objects just before Blink GC is triggered. </summary> @@ -14802,7 +14827,32 @@ </summary> </histogram> +<histogram name="BlinkGC.TimeForAtomicPhase" units="ms" expires_after="M80"> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> + <summary> + Duration of overall garbage collection time when the garbage collector is + invoked for finishing a garbage collection. This includes finishing up any + already running garbage collection operation. Recorded at the end of each + garbage collection. + </summary> +</histogram> + +<histogram name="BlinkGC.TimeForAtomicPhaseMarking" units="ms" + expires_after="M80"> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> + <summary> + Duration of finishing marking the transitive closure of objects during the + final Blink garbage collection pause. Recorded at the end of each garbage + collection. + </summary> +</histogram> + <histogram name="BlinkGC.TimeForCoalesce" units="ms"> + <obsolete> + Deprecated 07/2019. Coalescing is not used anymore. + </obsolete> <owner>haraken@chromium.org</owner> <owner>hpayer@chromium.org</owner> <summary> @@ -14820,7 +14870,8 @@ </summary> </histogram> -<histogram name="BlinkGC.TimeForGCCycle" units="ms"> +<histogram name="BlinkGC.TimeForGCCycle" units="ms" expires_after="M80"> + <owner>mlippautz@chromium.org</owner> <owner>oilpan-reviews@chromium.org</owner> <summary> Sum of all durations of individual phases within one Blink garbage @@ -14829,15 +14880,19 @@ </histogram> <histogram name="BlinkGC.TimeForGlobalWeakProcessing" units="ms" - expires_after="M77"> + expires_after="M80"> <owner>haraken@chromium.org</owner> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> <summary> Duration of time taken to run global weak processing of Blink GC. </summary> </histogram> -<histogram name="BlinkGC.TimeForHeapCompaction" units="ms"> +<histogram name="BlinkGC.TimeForHeapCompaction" units="ms" expires_after="M80"> <owner>haraken@chromium.org</owner> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> <summary> Duration of wall time taken to run BlinkGC's heap compaction. </summary> @@ -14853,8 +14908,11 @@ </summary> </histogram> -<histogram name="BlinkGC.TimeForInvokingPreFinalizers" units="ms"> +<histogram name="BlinkGC.TimeForInvokingPreFinalizers" units="ms" + expires_after="M80"> <owner>haraken@chromium.org</owner> + <owner>mlippautz@chromium.org</owner> + <owner>oilpan-reviews@chromium.org</owner> <summary> Duration of time taken to run ThreadState::invokePreFinalizers(). </summary> @@ -14870,7 +14928,8 @@ </summary> </histogram> -<histogram name="BlinkGC.TimeForNestedInV8" units="ms"> +<histogram name="BlinkGC.TimeForNestedInV8" units="ms" expires_after="M80"> + <owner>mlippautz@chromium.org</owner> <owner>oilpan-reviews@chromium.org</owner> <summary> Duration of the time of Blink garbage collection spent nested in a V8 @@ -92556,6 +92615,16 @@ </summary> </histogram> +<histogram name="PasswordGeneration.PresaveConflict" + enum="PasswordGenerationPresaveConflict" expires_after="M83"> + <owner>vasilii@chromium.org</owner> + <owner>dvadym@chromium.org</owner> + <summary> + Records whether there is a username conflict with existing credentials when + the user clicks 'Generate Password'. + </summary> +</histogram> + <histogram name="PasswordGeneration.SubmissionAvailableEvent" enum="PasswordSubmissionEvent"> <owner>dvadym@chromium.org</owner> @@ -94108,6 +94177,16 @@ </summary> </histogram> +<histogram name="PasswordManager.PresavedUpdateUIDismissalReason" + enum="PasswordManagerUIDismissalReason" expires_after="M83"> + <owner>vasilii@chromium.org</owner> + <owner>dvadym@chromium.org</owner> + <summary> + Why was the update password UI closed when a generated password caused a + conflict? + </summary> +</histogram> + <histogram name="PasswordManager.ProvisionalSaveFailure" enum="ProvisionalSaveFailure"> <owner>dvadym@chromium.org</owner> @@ -95325,6 +95404,11 @@ </summary> </histogram> +<histogram name="PaymentRequest.ServiceWorkerStatusCodeTimeout" enum="Boolean"> + <owner>sahel@chromium.org</owner> + <summary>True when a service worker times out 5 mins after request.</summary> +</histogram> + <histogram name="PaymentRequest.UserDidNotHaveCompleteSuggestionsForEverything.EffectOnCompletion" enum="PaymentRequestFlowCompletionStatus"> @@ -127637,7 +127721,7 @@ </histogram> <histogram name="Stability.CrashedProcessAge.Extension" units="ms" - expires_after="M77"> + expires_after="M83"> <owner>lukasza@chromium.org</owner> <summary> The age of a crashed extension process. Not logged on iOS. Logged together @@ -138988,7 +139072,7 @@ </summary> </histogram> -<histogram name="UMA.IsClonedInstall" enum="BooleanCloned" expires_after="M77"> +<histogram name="UMA.IsClonedInstall" enum="BooleanCloned" expires_after="M90"> <owner>asvitkine@chromium.org</owner> <summary> This is logged with a value of true in every log when the UMA client code @@ -142326,9 +142410,9 @@ </histogram> <histogram name="Variations.CreateTrials.SeedExpiry" - enum="VariationsSeedExpiry"> + enum="VariationsSeedExpiry" expires_after="M90"> <owner>asvitkine@chromium.org</owner> - <owner>fdoray@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The result of verifying if the variations seed is expired, recorded before trials are created from the seed. Expired seeds are treated as not existing. @@ -142396,8 +142480,9 @@ </histogram> <histogram name="Variations.FirstRun.SeedConnectTime" units="ms" - expires_after="M77"> + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The latency of connection to the variations server when fetching an initial variations seed during Android Chrome first run. This is included in the @@ -142407,8 +142492,9 @@ </histogram> <histogram name="Variations.FirstRun.SeedFetchResult" - enum="VariationsFirstRunSeedFetchResult"> + enum="VariationsFirstRunSeedFetchResult" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The result of attempting to fetch an initial variations seed during Android Chrome first run. Records both the HTTP code and various error values in one @@ -142417,8 +142503,9 @@ </histogram> <histogram name="Variations.FirstRun.SeedFetchTime" units="ms" - expires_after="M77"> + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The latency of fetching an initial variations seed during Android Chrome first run. Only considers cases where an HTTP 200 result was received. @@ -142426,8 +142513,9 @@ </histogram> <histogram name="Variations.FirstRunResult" enum="VariationsFirstRunResult" - expires_after="M77"> + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The result of attempting to import an initial variations seed during Android Chrome first run. Logged from VariationsSeedStore::LoadSeed when seed prefs @@ -142437,6 +142525,9 @@ <histogram name="Variations.GoogleUpdateRegistryLabelsNeedClearing" enum="BooleanNeedsClearing"> + <obsolete> + No longer logged + </obsolete> <owner>jwd@chromium.org</owner> <summary> If the registry value for Google Update experiment labels contains @@ -142447,6 +142538,9 @@ <histogram name="Variations.HeaderConstructionTime" units="microseconds" expires_after="M77"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> How long it took to create the X-Client-Data header. @@ -142459,8 +142553,9 @@ </summary> </histogram> -<histogram name="Variations.Headers.ExperimentCount"> +<histogram name="Variations.Headers.ExperimentCount" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records number of experiment ids in the X-Client-Data header at the time the header is constructed. @@ -142469,6 +142564,7 @@ <histogram name="Variations.Headers.URLValidationResult" enum="VariationsHeadersURLValidationResult" expires_after="M77"> + <owner>asvitkine@chromium.org</owner> <owner>jwd@chromium.org</owner> <summary> The result of the check of whether to append Variations headers to a url. @@ -142476,8 +142572,9 @@ </histogram> <histogram name="Variations.LoadPermanentConsistencyCountryResult" - enum="VariationsPermanentConsistencyCountryResult"> - <owner>sclittle@chromium.org</owner> + enum="VariationsPermanentConsistencyCountryResult" expires_after="M90"> + <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records how the country code saved in prefs used for filtering permanent consistency studies compares to the country code in the variations seed. @@ -142488,6 +142585,7 @@ <histogram name="Variations.LoadSeedSignature" enum="VariationSeedSignature" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The result of verifying the latest variations seed's signature, recorded when the seed is loaded from Local State. Not recorded when running in safe @@ -142509,6 +142607,7 @@ <histogram name="Variations.RequestCount" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Number of previous requests to the variations server in the same session, logged each time a new request is attempted to the variations server. For @@ -142518,11 +142617,13 @@ </histogram> <histogram name="Variations.ResourceRequestsAllowed" - enum="VariationsResourceRequestsAllowedState" expires_after="M77"> + enum="VariationsResourceRequestsAllowedState" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Counts the number of times the VariationsService is allowed or not allowed - to make a request due to the ResourceRequestAllowedNotifier. + to make a request due to the ResourceRequestAllowedNotifier. Useful for + debugging cases where variations seeds may not be getting fetched. </summary> </histogram> @@ -142545,8 +142646,9 @@ </histogram> <histogram name="Variations.SafeMode.FellBackToSafeMode2" - enum="BooleanSafeMode"> + enum="BooleanSafeMode" expires_after="M90"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> Whether or not the VariationsService fell back to Safe Mode, due to either too many crashes or too many failures to fetch a new seed. Recorded during @@ -142558,6 +142660,7 @@ <histogram name="Variations.SafeMode.LoadSafeSeed.Result" enum="VariationsSeedLoadResult" expires_after="M77"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> Records whether the safe variations seed was successfully read from local state on startup. Records a detailed reason on read failure. Only recorded @@ -142568,6 +142671,7 @@ <histogram name="Variations.SafeMode.LoadSafeSeed.SignatureValidity" enum="VariationSeedSignature" expires_after="M77"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> The result of verifying the safe variations seed's signature, recorded when the seed is loaded from Local State. Only recorded when attempting to run in @@ -142578,6 +142682,7 @@ <histogram name="Variations.SafeMode.StoreSafeSeed.Result" enum="VariationsSeedStoreResult" expires_after="M77"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> Records the result of storing a safe variations seed (and all associated metadata) to the seed store. @@ -142587,14 +142692,17 @@ <histogram name="Variations.SafeMode.StoreSafeSeed.SignatureValidity" enum="VariationSeedSignature" expires_after="M77"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> The result of verifying the safe variations seed's signature, recorded when the safe seed is stored to Local State. </summary> </histogram> -<histogram name="Variations.SafeMode.Streak.Crashes" units="crashes"> +<histogram name="Variations.SafeMode.Streak.Crashes" units="crashes" + expires_after="M90"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> The number of consecutive crashes observed by the VariationsService, without a single intervening successful seed fetch. Recorded during Chrome startup, @@ -142602,8 +142710,10 @@ </summary> </histogram> -<histogram name="Variations.SafeMode.Streak.FetchFailures" units="failures"> +<histogram name="Variations.SafeMode.Streak.FetchFailures" units="failures" + expires_after="M90"> <owner>isherman@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> The number of consecutive failed attempts to fetch a new seed by the VariationsService. Recorded during Chrome startup, when the @@ -142611,8 +142721,10 @@ </summary> </histogram> -<histogram name="Variations.SeedDateChange" enum="VariationsSeedDateChange"> +<histogram name="Variations.SeedDateChange" enum="VariationsSeedDateChange" + expires_after="M90"> <owner>jwd@chromium.org</owner> + <owner>asvitkine@chromium.org</owner> <summary> Counts if a response from the variations server is the first response of the day or not. This is counted when a new valid seed or a 304 is received. The @@ -142705,8 +142817,9 @@ </histogram> <histogram name="Variations.SeedFetchResponseOrErrorCode" - enum="CombinedHttpResponseAndNetErrorCode"> + enum="CombinedHttpResponseAndNetErrorCode" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> HTTP response codes and network error encountered by VariationsService when attempting to fetch a variations seed from the server over an HTTPS @@ -142715,8 +142828,9 @@ </histogram> <histogram name="Variations.SeedFetchResponseOrErrorCode.HTTP" - enum="CombinedHttpResponseAndNetErrorCode"> + enum="CombinedHttpResponseAndNetErrorCode" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> HTTP response codes and network error encountered by VariationsService when attempting to fetch a variations seed from the server over an HTTP @@ -142724,8 +142838,9 @@ </summary> </histogram> -<histogram name="Variations.SeedFreshness" units="minutes"> +<histogram name="Variations.SeedFreshness" units="minutes" expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The time interval between when the variations seed was downloaded and when it was loaded for use. Not recorded for expired seeds, nor when the download @@ -142733,7 +142848,8 @@ </summary> </histogram> -<histogram name="Variations.SeedLoadBlockingTime" units="ms"> +<histogram name="Variations.SeedLoadBlockingTime" units="ms" + expires_after="M90"> <owner>paulmiller@chromium.org</owner> <owner>changwan@chromium.org</owner> <summary> @@ -142747,8 +142863,10 @@ </summary> </histogram> -<histogram name="Variations.SeedLoadResult" enum="VariationsSeedLoadResult"> +<histogram name="Variations.SeedLoadResult" enum="VariationsSeedLoadResult" + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records whether the latest variations seed was successfully read from local state on startup. Records a detailed reason on read failure. Not recorded @@ -142795,11 +142913,13 @@ </summary> </histogram> -<histogram name="Variations.SeedStoreResult" enum="VariationsSeedStoreResult"> +<histogram name="Variations.SeedStoreResult" enum="VariationsSeedStoreResult" + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records the result of storing the variations seed that was received from the - server. + server. Also logs the types of data received (gzip, delta, etc). </summary> </histogram> @@ -142818,6 +142938,7 @@ <histogram name="Variations.SimulateSeed.Duration" units="ms" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records the time taken to perform variations seed simulation. @@ -142828,6 +142949,7 @@ <histogram name="Variations.SimulateSeed.KillBestEffortChanges" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records the result of variations seed simulation. Logs the number of experiment groups in the "kill best effort" category that are @@ -142840,6 +142962,7 @@ <histogram name="Variations.SimulateSeed.KillCriticalChanges" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records the result of variations seed simulation. Logs the number of experiment groups in the "kill critical" category that are @@ -142852,6 +142975,7 @@ <histogram name="Variations.SimulateSeed.NormalChanges" expires_after="2018-08-30"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Records the result of variations seed simulation. Logs the number of experiment groups in the "normal" category that are expected to @@ -142861,8 +142985,20 @@ </summary> </histogram> +<histogram name="Variations.StoreSeed.DataSize" units="KiB" expires_after="M90"> + <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> + <summary> + The size of the variations seed data, which may be a partial (delta) or + compressed (gzip) payload, see Variations.SeedStoreResult. + </summary> +</histogram> + <histogram name="Variations.StoreSeed.DeltaSize" units="KiB" expires_after="M77"> + <obsolete> + Deprecated 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> On successful save of a delta-compressed variations seed, records the size @@ -142872,6 +143008,9 @@ <histogram name="Variations.StoreSeed.DeltaSize.ReductionPercent" units="%" expires_after="M77"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> On successful save of a delta-compressed variations seed, records the size @@ -142880,6 +143019,9 @@ </histogram> <histogram name="Variations.StoreSeed.GzipSize" units="KiB"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> Records the size of the gzip-compressed variations seed in KiB. @@ -142887,6 +143029,9 @@ </histogram> <histogram name="Variations.StoreSeed.GzipSize.ReductionPercent" units="%"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> Records the size of the gzip-compressed variations seed as a percentage of @@ -142897,6 +143042,9 @@ </histogram> <histogram name="Variations.StoreSeed.HasCountry" enum="Boolean"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> Records whether a country code was present when storing the variations seed. @@ -142904,6 +143052,9 @@ </histogram> <histogram name="Variations.StoreSeed.Size" units="KiB"> + <obsolete> + Removed 2019/07 + </obsolete> <owner>asvitkine@chromium.org</owner> <summary> On successful save of a non-delta-compressed variations seed, records the @@ -142911,8 +143062,10 @@ </summary> </histogram> -<histogram name="Variations.StoreSeedSignature" enum="VariationSeedSignature"> +<histogram name="Variations.StoreSeedSignature" enum="VariationSeedSignature" + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The result of verifying the variations seed signature, recorded when the variations seed is stored to Local State after being retrieved from the @@ -142929,8 +143082,10 @@ </summary> </histogram> -<histogram name="Variations.TimeSinceLastFetchAttempt" units="minutes"> +<histogram name="Variations.TimeSinceLastFetchAttempt" units="minutes" + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> The time since the previous attempt to fetch the variations seed within the same session, with 0 indicating that this is the first attempt. Recorded @@ -142963,8 +143118,10 @@ </summary> </histogram> -<histogram name="Variations.UserChannel" enum="UserChannels"> +<histogram name="Variations.UserChannel" enum="UserChannels" + expires_after="M90"> <owner>asvitkine@chromium.org</owner> + <owner>rkaplow@chromium.org</owner> <summary> Log the user channel assigned at browser startup used for evaluating the variations seeds.
diff --git a/tools/perf/contrib/orderfile/OWNERS b/tools/perf/contrib/orderfile/OWNERS index d45b803a..3301555 100644 --- a/tools/perf/contrib/orderfile/OWNERS +++ b/tools/perf/contrib/orderfile/OWNERS
@@ -1,3 +1,2 @@ lizeb@chromium.org -mattcary@chromium.org pasko@chromium.org
diff --git a/tools/perf/core/minidump_unittest.py b/tools/perf/core/minidump_unittest.py index 47870df1..58fbb53 100644 --- a/tools/perf/core/minidump_unittest.py +++ b/tools/perf/core/minidump_unittest.py
@@ -88,7 +88,9 @@ self.assertTrue(crash_function in sections[4]) @decorators.Isolated - @decorators.Disabled('chromeos', 'android', 'win7') + # Disabled on Mac 10.12 (Sierra) due to it not getting a stack trace to + # symbolize from the second crash. + @decorators.Disabled('chromeos', 'android', 'win7', 'sierra') def testMultipleCrashMinidumps(self): # Wait for the browser to restart fully before crashing self._LoadPageThenWait('var cat = "dog";', 'cat')
diff --git a/tools/polymer/polymer.py b/tools/polymer/polymer.py index d70a93f..adab3591 100644 --- a/tools/polymer/polymer.py +++ b/tools/polymer/polymer.py
@@ -297,21 +297,46 @@ html_template = _extract_template(html_file, 'dom-module') js_imports = _generate_js_imports(html_file) + IIFE_OPENING = '(function() {\n' + IIFE_CLOSING = '})();' + with open(js_file) as f: lines = f.readlines() + imports_added = False + iife_found = False + for i, line in enumerate(lines): - # Place the JS imports right before the opening "Polymer({" line. - # Note: Currently assuming there is only one Polymer declaration per page, - # and no other code precedes it. + if not imports_added: + if line.startswith(IIFE_OPENING): + # Replace the IIFE opening line with the JS imports. + line = '\n'.join(js_imports) + '\n\n' + imports_added = True + iife_found = True + elif line.startswith('Polymer({\n'): + # Place the JS imports right before the opening "Polymer({" line. + line = line.replace( + r'Polymer({', '%s\n\nPolymer({' % '\n'.join(js_imports)) + imports_added = True + # Place the HTML content right after the opening "Polymer({" line. + # Note: There is currently an assumption that only one Polymer() declaration + # exists per file. line = line.replace( r'Polymer({', - '%s\nPolymer({\n _template: html`%s`,' % ( - '\n'.join(js_imports) + '\n', html_template)) + 'Polymer({\n _template: html`%s`,' % html_template) + + if line.startswith('cr.exportPath('): + line = '' + line = _rewrite_namespaces(line) lines[i] = line + if iife_found: + last_line = lines[-1] + assert last_line.startswith(IIFE_CLOSING), 'Could not detect IIFE closing' + lines[-1] = '' + # Use .m.js extension for the generated JS file, since both files need to be # served by a chrome:// URL side-by-side. out_filename = os.path.basename(js_file).replace('.js', '.m.js')
diff --git a/tools/polymer/polymer_test.py b/tools/polymer/polymer_test.py index f4039f4..95dfa68 100755 --- a/tools/polymer/polymer_test.py +++ b/tools/polymer/polymer_test.py
@@ -52,6 +52,13 @@ 'dom-module', 'dom_module.html', 'dom_module.js', 'dom_module.m.js', 'dom_module_expected.js') + # Test case where HTML is extracted from a Polymer2 <dom-module> that is + # wrapped in an IIFE function. + def testDomModuleIife(self): + self._run_test( + 'dom-module', 'dom_module.html', 'dom_module_iife.js', + 'dom_module_iife.m.js', 'dom_module_iife_expected.js') + # Test case where HTML is extracted from a Polymer2 style module. def testStyleModule(self): self._run_test(
diff --git a/tools/polymer/tests/dom_module_iife.js b/tools/polymer/tests/dom_module_iife.js new file mode 100644 index 0000000..34375f1 --- /dev/null +++ b/tools/polymer/tests/dom_module_iife.js
@@ -0,0 +1,8 @@ +(function() { +const foo = 'foo'; + +Polymer({ + is: 'cr-test-foo', + behaviors: [Polymer.PaperRippleBehavior], +}); +})();
diff --git a/tools/polymer/tests/dom_module_iife_expected.js b/tools/polymer/tests/dom_module_iife_expected.js new file mode 100644 index 0000000..c59d82b6 --- /dev/null +++ b/tools/polymer/tests/dom_module_iife_expected.js
@@ -0,0 +1,19 @@ +import {Polymer, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {PaperRippleBehavior} from 'chrome://resources/polymer/v3_0/paper-behaviors/paper-ripple-behavior.js'; +import '../shared_vars_css.m.js'; +import './foo.m.js'; + +const foo = 'foo'; + +Polymer({ + _template: html` + <style> + div { + font-size: 2rem; + } + </style> + <div>Hello world</div> +`, + is: 'cr-test-foo', + behaviors: [PaperRippleBehavior], +});
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index e390f9e..4c8281b 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -15,6 +15,7 @@ #include <vector> #include "base/command_line.h" +#include "base/debug/leak_annotations.h" #include "base/no_destructor.h" #include "base/optional.h" #include "base/strings/string_number_conversions.h" @@ -23,6 +24,7 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_mode_observer.h" #include "ui/accessibility/ax_node_data.h" @@ -285,6 +287,16 @@ } std::string result = base::JoinString(names, " "); + +#if defined(LEAK_SANITIZER) && !defined(OS_NACL) + // http://crbug.com/982839 + // atk_table_get_column_description and atk_table_get_row_description return + // const gchar*, which suggests the caller does not gain ownership of the + // returned string. The g_strdup below causes a new allocation, which does not + // fit that pattern and causes a leak in tests. + ScopedLeakSanitizerDisabler lsan_disabler; +#endif + return g_strdup(result.c_str()); }
diff --git a/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java b/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java index 25aef12..49ced1db 100644 --- a/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java +++ b/ui/android/java/src/org/chromium/ui/base/ResourceBundle.java
@@ -4,7 +4,6 @@ package org.chromium.ui.base; -import org.chromium.base.BuildConfig; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -13,14 +12,52 @@ /** * This class provides the resource bundle related methods for the native * library. + * + * IMPORTANT: Clients that use {@link ResourceBundle} and/or + * {@link org.chromium.ui.resources.ResourceExtractor} MUST call either + * {@link ResourceBundle#setAvailablePakLocales(String[], String[])} or + * {@link ResourceBundle#setNoAvailableLocalePaks()} before calling the getters in this class. */ @JNINamespace("ui") -final class ResourceBundle { +public final class ResourceBundle { + private static String[] sCompressedLocales; + private static String[] sUncompressedLocales; + private ResourceBundle() {} + /** + * Called when there are no locale pak files available. + */ + public static void setNoAvailableLocalePaks() { + assert sCompressedLocales == null && sUncompressedLocales == null; + sCompressedLocales = new String[] {}; + sUncompressedLocales = new String[] {}; + } + + /** + * Sets the available compressed and uncompressed locale pak files. + * @param compressed Locales that have compressed pak files. + * @param uncompressed Locales that have uncompressed pak files. + */ + public static void setAvailablePakLocales(String[] compressed, String[] uncompressed) { + assert sCompressedLocales == null && sUncompressedLocales == null; + sCompressedLocales = compressed; + sUncompressedLocales = uncompressed; + } + + /** + * Return the array of locales that have compressed pak files. Do not modify the array. + * @return The locales that have compressed pak files. + */ + public static String[] getAvailableCompressedPakLocales() { + assert sCompressedLocales != null; + return sCompressedLocales; + } + @CalledByNative private static String getLocalePakResourcePath(String locale) { - if (Arrays.binarySearch(BuildConfig.UNCOMPRESSED_LOCALES, locale) >= 0) { + assert sUncompressedLocales != null; + if (Arrays.binarySearch(sUncompressedLocales, locale) >= 0) { return "assets/stored-locales/" + locale + ".pak"; } return null;
diff --git a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java index 80c64ed..8cb4eae 100644 --- a/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java +++ b/ui/android/java/src/org/chromium/ui/resources/ResourceExtractor.java
@@ -6,7 +6,6 @@ import android.content.res.AssetManager; -import org.chromium.base.BuildConfig; import org.chromium.base.BuildInfo; import org.chromium.base.ContextUtils; import org.chromium.base.FileUtils; @@ -18,6 +17,7 @@ import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; import org.chromium.ui.base.LocalizationUtils; +import org.chromium.ui.base.ResourceBundle; import java.io.File; import java.io.IOException; @@ -158,14 +158,15 @@ // Currenty (Apr 2018), this array can be as big as 6 entries, so using a capacity // that allows a bit of growth, but is still in the right ballpark.. ArrayList<String> activeLocales = new ArrayList<String>(6); - for (String locale : BuildConfig.COMPRESSED_LOCALES) { + String[] compressedLocales = ResourceBundle.getAvailableCompressedPakLocales(); + for (String locale : compressedLocales) { if (LocalizationUtils.chromiumLocaleMatchesLanguage(locale, uiLanguage)) { activeLocales.add(locale); } } if (activeLocales.isEmpty()) { - assert BuildConfig.COMPRESSED_LOCALES.length > 0; - assert Arrays.asList(BuildConfig.COMPRESSED_LOCALES).contains(FALLBACK_LOCALE); + assert compressedLocales.length > 0; + assert Arrays.asList(compressedLocales).contains(FALLBACK_LOCALE); activeLocales.add(FALLBACK_LOCALE); } @@ -357,6 +358,6 @@ private static boolean shouldSkipPakExtraction() { // Certain apks like ContentShell.apk don't have any compressed locale // assets however, so skip extraction entirely for them. - return BuildConfig.COMPRESSED_LOCALES.length == 0; + return ResourceBundle.getAvailableCompressedPakLocales().length == 0; } }
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc index 1e12f53..ebdbc11 100644 --- a/ui/events/blink/input_handler_proxy.cc +++ b/ui/events/blink/input_handler_proxy.cc
@@ -1103,15 +1103,6 @@ scroll_predictor_->ResampleScrollEvents(compositor_event_queue_->Pop(), args.frame_time); - // TODO(eirage): Remove this component as it's same as - // LATENCY_BEGIN_FRAME_RENDERER_*. - // Save the rAF time in the latency info to be able to compute the - // output latency. - LatencyInfo* latency_info = event_with_callback->mutable_latency_info(); - latency_info->AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT, - args.frame_time); - DispatchSingleInputEvent(std::move(event_with_callback), args.frame_time); } }
diff --git a/ui/latency/latency_info.cc b/ui/latency/latency_info.cc index c1ef1d7..29dceca 100644 --- a/ui/latency/latency_info.cc +++ b/ui/latency/latency_info.cc
@@ -39,7 +39,6 @@ CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_IMPL_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT); - CASE_TYPE(INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_MAIN_COMPONENT); CASE_TYPE(INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT);
diff --git a/ui/latency/latency_info.h b/ui/latency/latency_info.h index 333f0590837..68e31c1 100644 --- a/ui/latency/latency_info.h +++ b/ui/latency/latency_info.h
@@ -77,8 +77,6 @@ INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, // Original timestamp of the last event that has been coalesced into this one. INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, - // Timestamp of the frame when a scroll update event is handled (rAF time). - INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT, // Timestamp when the event's ack is received by the RWH. INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, // Timestamp when the frame is swapped in renderer.
diff --git a/ui/latency/latency_tracker.cc b/ui/latency/latency_tracker.cc index 376444b..67b829da 100644 --- a/ui/latency/latency_tracker.cc +++ b/ui/latency/latency_tracker.cc
@@ -263,26 +263,6 @@ DCHECK(scroll_name == "ScrollBegin" || scroll_name == "ScrollUpdate" || (IsInertialScroll(latency) && scroll_name == "ScrollInertial")); - if (!IsInertialScroll(latency) && input_modality == "Touch") { - average_lag_tracker_.AddLatencyInFrame(latency, gpu_swap_begin_timestamp, - scroll_name); - - base::TimeTicks last_scroll_event_timestamp, frame_time; - if (latency.FindLatency( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, - &last_scroll_event_timestamp) && - latency.FindLatency( - ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT, - &frame_time)) { - UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( - "Event.Latency." + scroll_name + ".Touch.EventTimeToRAFTime", - last_scroll_event_timestamp, frame_time); - UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( - "Event.Latency." + scroll_name + ".Touch.RAFTimeToFrameSwapEnd", - frame_time, gpu_swap_end_timestamp); - } - } - base::TimeTicks rendering_scheduled_timestamp; bool rendering_scheduled_on_main = latency.FindLatency( ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_MAIN_COMPONENT, @@ -294,6 +274,24 @@ DCHECK_AND_RETURN_ON_FAIL(found_component); } + if (!IsInertialScroll(latency) && input_modality == "Touch") { + average_lag_tracker_.AddLatencyInFrame(latency, gpu_swap_begin_timestamp, + scroll_name); + + base::TimeTicks last_scroll_event_timestamp; + if (!rendering_scheduled_on_main && + latency.FindLatency( + ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, + &last_scroll_event_timestamp)) { + UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( + "Event.Latency." + scroll_name + ".Touch.EventTimeToRAFTime", + last_scroll_event_timestamp, rendering_scheduled_timestamp); + UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( + "Event.Latency." + scroll_name + ".Touch.RAFTimeToFrameSwapEnd", + rendering_scheduled_timestamp, gpu_swap_end_timestamp); + } + } + // Inertial and scrollbar scrolls are excluded from Ukm metrics. if ((input_modality == "Touch" && !IsInertialScroll(latency)) || input_modality == "Wheel") {
diff --git a/ui/latency/mojo/latency_info.mojom b/ui/latency/mojo/latency_info.mojom index 7f844bb..6fb50d7 100644 --- a/ui/latency/mojo/latency_info.mojom +++ b/ui/latency/mojo/latency_info.mojom
@@ -44,8 +44,6 @@ INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, // Timestamp for last event that has been coalesced into this one. INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, - // Timestamp of the frame when a scroll update event is handled (rAF time). - INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT, // Timestamp when the event's ack is received by the RWH. INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT, // Timestamp when the frame is swapped in renderer.
diff --git a/ui/latency/mojo/latency_info_struct_traits.cc b/ui/latency/mojo/latency_info_struct_traits.cc index c93fe09..4bb11f1 100644 --- a/ui/latency/mojo/latency_info_struct_traits.cc +++ b/ui/latency/mojo/latency_info_struct_traits.cc
@@ -206,9 +206,6 @@ case ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT: return ui::mojom::LatencyComponentType:: INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT; - case ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT: - return ui::mojom::LatencyComponentType:: - INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT; case ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT: return ui::mojom::LatencyComponentType:: INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT; @@ -296,10 +293,6 @@ INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT: *output = ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT; return true; - case ui::mojom::LatencyComponentType:: - INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT: - *output = ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_RAF_TIME_COMPONENT; - return true; case ui::mojom::LatencyComponentType::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT: *output = ui::INPUT_EVENT_LATENCY_ACK_RWH_COMPONENT; return true;
diff --git a/ui/login/account_picker/user_pod_template.html b/ui/login/account_picker/user_pod_template.html index ef32ee9..0c68249 100644 --- a/ui/login/account_picker/user_pod_template.html +++ b/ui/login/account_picker/user_pod_template.html
@@ -215,10 +215,10 @@ </div> <div class="horizontal-line"></div> <div class="bottom-container"> - <paper-button raised class="enter-button" + <cr-button class="action-button enter-button" aria-label="$i18n{publicAccountEnterAccessibleName}"> $i18n{publicAccountEnter} - </paper-button> + </cr-button> <div class="language-and-input-container"> <a class="language-and-input" href="#" role="button"> $i18n{publicSessionLanguageAndInput}
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc index 203a747..a47d09b 100644 --- a/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -121,8 +121,6 @@ DISALLOW_COPY_AND_ASSIGN(MdTabStrip); }; -// static -const char Tab::kViewClassName[] = "Tab"; Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) : tabbed_pane_(tabbed_pane), @@ -272,10 +270,6 @@ return size; } -const char* Tab::GetClassName() const { - return kViewClassName; -} - void Tab::SetState(State state) { if (state == state_) return; @@ -358,6 +352,10 @@ tabbed_pane_->MoveSelectionBy(key == ui::VKEY_DOWN ? 1 : -1); } +BEGIN_METADATA(Tab) +METADATA_PARENT_CLASS(View) +END_METADATA() + MdTab::MdTab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) @@ -428,7 +426,6 @@ // static constexpr size_t TabStrip::kNoSelectedTab; -const char TabStrip::kViewClassName[] = "TabStrip"; TabStrip::TabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style) @@ -459,10 +456,6 @@ void TabStrip::OnSelectedTabChanged(Tab* from_tab, Tab* to_tab) {} -const char* TabStrip::GetClassName() const { - return kViewClassName; -} - void TabStrip::OnPaintBorder(gfx::Canvas* canvas) { // Do not draw border line in kHighlight mode. if (style_ == TabbedPane::TabStripStyle::kHighlight) @@ -558,6 +551,33 @@ num_children); } +TabbedPane::Orientation TabStrip::GetOrientation() const { + return orientation_; +} + +TabbedPane::TabStripStyle TabStrip::GetStyle() const { + return style_; +} + +DEFINE_ENUM_CONVERTERS(TabbedPane::Orientation, + {TabbedPane::Orientation::kHorizontal, + base::ASCIIToUTF16("HORIZONTAL")}, + {TabbedPane::Orientation::kVertical, + base::ASCIIToUTF16("VERTICAL")}) + +DEFINE_ENUM_CONVERTERS(TabbedPane::TabStripStyle, + {TabbedPane::TabStripStyle::kBorder, + base::ASCIIToUTF16("BORDER")}, + {TabbedPane::TabStripStyle::kHighlight, + base::ASCIIToUTF16("HIGHLIGHT")}) + +BEGIN_METADATA(TabStrip) +METADATA_PARENT_CLASS(View) +ADD_READONLY_PROPERTY_METADATA(TabStrip, int, SelectedTabIndex) +ADD_READONLY_PROPERTY_METADATA(TabStrip, TabbedPane::Orientation, Orientation) +ADD_READONLY_PROPERTY_METADATA(TabStrip, TabbedPane::TabStripStyle, Style) +END_METADATA() + MdTabStrip::MdTabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style) : TabStrip(orientation, style) { @@ -586,7 +606,7 @@ DCHECK(!from_tab->selected()); DCHECK(to_tab->selected()); - if (orientation() == TabbedPane::Orientation::kHorizontal) { + if (GetOrientation() == TabbedPane::Orientation::kHorizontal) { animating_from_ = gfx::Range(from_tab->GetMirroredX(), from_tab->GetMirroredX() + from_tab->width()); animating_to_ = gfx::Range(to_tab->GetMirroredX(), @@ -604,13 +624,13 @@ void MdTabStrip::OnPaintBorder(gfx::Canvas* canvas) { // Do not draw border line in kHighlight mode. - if (style() == TabbedPane::TabStripStyle::kHighlight) + if (GetStyle() == TabbedPane::TabStripStyle::kHighlight) return; constexpr int kUnselectedBorderThickness = 1; constexpr int kSelectedBorderThickness = 2; const bool is_horizontal = - orientation() == TabbedPane::Orientation::kHorizontal; + GetOrientation() == TabbedPane::Orientation::kHorizontal; int max_cross_axis; @@ -785,11 +805,11 @@ } TabbedPane::Orientation TabbedPane::GetOrientation() const { - return tab_strip_->orientation(); + return tab_strip_->GetOrientation(); } TabbedPane::TabStripStyle TabbedPane::GetStyle() const { - return tab_strip_->style(); + return tab_strip_->GetStyle(); } Tab* TabbedPane::GetTabAt(size_t index) {
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h index 7020c4d..ec2d831 100644 --- a/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -143,8 +143,7 @@ // The tab view shown in the tab strip. class VIEWS_EXPORT Tab : public View { public: - // Internal class name. - static const char kViewClassName[]; + METADATA_HEADER(Tab); Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents); ~Tab() override; @@ -163,7 +162,6 @@ void OnMouseExited(const ui::MouseEvent& event) override; void OnGestureEvent(ui::GestureEvent* event) override; gfx::Size CalculatePreferredSize() const override; - const char* GetClassName() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; bool HandleAccessibleAction(const ui::AXActionData& action_data) override; void OnFocus() override; @@ -203,12 +201,11 @@ // The tab strip shown above/left of the tab contents. class TabStrip : public View { public: + METADATA_HEADER(TabStrip); + // The return value of GetSelectedTabIndex() when no tab is selected. static constexpr size_t kNoSelectedTab = size_t{-1}; - // Internal class name. - static const char kViewClassName[]; - TabStrip(TabbedPane::Orientation orientation, TabbedPane::TabStripStyle style); ~TabStrip() override; @@ -219,7 +216,6 @@ virtual void OnSelectedTabChanged(Tab* from_tab, Tab* to_tab); // Overridden from View: - const char* GetClassName() const override; void OnPaintBorder(gfx::Canvas* canvas) override; Tab* GetSelectedTab() const; @@ -227,9 +223,9 @@ Tab* GetTabAtIndex(size_t index) const; size_t GetSelectedTabIndex() const; - TabbedPane::Orientation orientation() const { return orientation_; } + TabbedPane::Orientation GetOrientation() const; - TabbedPane::TabStripStyle style() const { return style_; } + TabbedPane::TabStripStyle GetStyle() const; private: // The orientation of the tab alignment.
diff --git a/ui/views/layout/flex_layout.cc b/ui/views/layout/flex_layout.cc index b82cd59..8c76abb2 100644 --- a/ui/views/layout/flex_layout.cc +++ b/ui/views/layout/flex_layout.cc
@@ -28,135 +28,57 @@ namespace views { -namespace internal { +using internal::NormalizedInsets; +using internal::NormalizedPoint; +using internal::NormalizedRect; +using internal::NormalizedSize; +using internal::NormalizedSizeBounds; + +using internal::Denormalize; +using internal::Normalize; namespace { // Layout information for a specific child view in a proposed layout. -struct ChildLayout { - ChildLayout(View* view, const FlexSpecification& flex) - : view(view), flex(flex) {} - ChildLayout(ChildLayout&& other) = default; +struct FlexChildData { + explicit FlexChildData(const FlexSpecification& flex) : flex(flex) {} + FlexChildData(FlexChildData&& other) = default; - View* view = nullptr; - bool excluded = false; - // Indicates whether external code has called SetVisible(false) on the view. - bool hidden_by_owner = false; - // Indicates whether the layout has chosen to display this child view. - bool visible = false; // Start with zero size rather than unspecified size bounds because we start // all layouts with controls at their minimum allowed sizes. NormalizedSizeBounds available_size{0, 0}; NormalizedSize preferred_size; NormalizedSize current_size; - NormalizedRect actual_bounds; NormalizedInsets margins; NormalizedInsets internal_padding; + NormalizedRect actual_bounds; FlexSpecification flex; private: // Copying this struct would be expensive and they only ever live in a vector // in Layout (see below) so we'll only allow move semantics. - DISALLOW_COPY_AND_ASSIGN(ChildLayout); + DISALLOW_COPY_AND_ASSIGN(FlexChildData); }; -// Represents a specific stored layout given a set of size bounds. -struct Layout { - explicit Layout(int64_t layout_counter) : layout_id(layout_counter) {} - - // Indicates which layout pass this layout was created/last validated on. If - // this number disagrees with FlexLayoutInternal::layout_counter_, we need to - // re-validate the layout before using it. - mutable uint32_t layout_id; - std::vector<ChildLayout> child_layouts; - NormalizedInsets interior_margin; - NormalizedSizeBounds available_size; - NormalizedSize total_size; - - private: - DISALLOW_COPY_AND_ASSIGN(Layout); -}; - -// Preserves layouts for common layout requests, because we may be asked to -// recompute them many times but don't want to repeat the calculation. -// -// Specifically, we will be asked for: -// - The layout's minimum size (bounds = {0, 0}) -// - The layout's preferred size (neither bound specified) -// - The final layout at the dimensions of the host view (both bounds nonzero) -// -// There is also the possibility of a view with a flex layout being embedded in -// a view with a flex layout, where the inner view has a nontrivial flex -// specification. In that case, in order to handle e.g. labels in the inner -// view, the outer layout manager will ask for the inner view's preferred height -// for its width, which potentially involves another layout calculation. -// However, these calculations are transient - they only happen when the layout -// is recalculated or validated, and in the latter case only one set of bounds -// is evaluated. So we will keep exactly one of these layouts around. -// -// As a first pass, we'll store all of these specifically. In the future we can -// switch to using something more sophisticated, like an MRU cache, if we find -// we're thrashing for some other reason. -class LayoutCache { - public: - // Removes all saved layouts. - void Clear() { - minimum_.reset(); - preferred_.reset(); - intermediate_.layout.reset(); - current_.layout.reset(); - } - - // Gets a cached layout, or nullptr if no layout is cached for the specified - // bounds. - Layout* Get(const NormalizedSizeBounds& bounds) const { - if (bounds == NormalizedSizeBounds(0, 0)) - return minimum_.get(); - if (bounds == NormalizedSizeBounds()) - return preferred_.get(); - if (bounds == intermediate_.bounds) - return intermediate_.layout.get(); - if (bounds == current_.bounds) - return current_.layout.get(); - return nullptr; - } - - // Caches a layout associated with the specified bounds. - void Put(const NormalizedSizeBounds& bounds, std::unique_ptr<Layout> layout) { - if (bounds == NormalizedSizeBounds(0, 0)) { - minimum_ = std::move(layout); - } else if (bounds == NormalizedSizeBounds()) { - preferred_ = std::move(layout); - } else if (!bounds.cross() || !bounds.main()) { - intermediate_.bounds = bounds; - intermediate_.layout = std::move(layout); - } else { - current_.bounds = bounds; - current_.layout = std::move(layout); - } - } - - private: - struct CacheEntry { - NormalizedSizeBounds bounds; - std::unique_ptr<Layout> layout; - }; - - std::unique_ptr<Layout> minimum_; - std::unique_ptr<Layout> preferred_; - - // TODO(dfried): consider replacing these with an MRUCache if this ends up - // thrashing a lot (see note above). - CacheEntry intermediate_; - CacheEntry current_; -}; +template <typename T> +T GetViewProperty(const View* view, + const ui::PropertyHandler& defaults, + const ui::ClassProperty<T*>* property) { + T* found_value = view->GetProperty(property); + if (found_value) + return *found_value; + found_value = defaults.GetProperty(property); + if (found_value) + return *found_value; + return T(); +} } // anonymous namespace // Private implementation ------------------------------------------------------ // Calculates and maintains 1D spacing between a sequence of child views. -class ChildViewSpacing { +class FlexLayout::ChildViewSpacing { public: // Given the indices of two child views, returns the amount of space that // should be placed between them if they were adjacent. If the first index is @@ -199,16 +121,18 @@ int trailing_space_; }; -ChildViewSpacing::ChildViewSpacing(GetViewSpacingCallback get_view_spacing) +FlexLayout::ChildViewSpacing::ChildViewSpacing( + GetViewSpacingCallback get_view_spacing) : get_view_spacing_(std::move(get_view_spacing)), trailing_space_(get_view_spacing_.Run(base::nullopt, base::nullopt)) {} -ChildViewSpacing::ChildViewSpacing(ChildViewSpacing&& other) +FlexLayout::ChildViewSpacing::ChildViewSpacing(ChildViewSpacing&& other) : get_view_spacing_(std::move(other.get_view_spacing_)), leading_spacings_(std::move(other.leading_spacings_)), trailing_space_(other.trailing_space_) {} -ChildViewSpacing& ChildViewSpacing::operator=(ChildViewSpacing&& other) { +FlexLayout::ChildViewSpacing& FlexLayout::ChildViewSpacing::operator=( + ChildViewSpacing&& other) { if (this != &other) { get_view_spacing_ = std::move(other.get_view_spacing_); leading_spacings_ = std::move(other.leading_spacings_); @@ -217,33 +141,33 @@ return *this; } -bool ChildViewSpacing::HasViewIndex(size_t view_index) const { +bool FlexLayout::ChildViewSpacing::HasViewIndex(size_t view_index) const { return leading_spacings_.find(view_index) != leading_spacings_.end(); } -int ChildViewSpacing::GetLeadingInset() const { +int FlexLayout::ChildViewSpacing::GetLeadingInset() const { if (leading_spacings_.empty()) return 0; return leading_spacings_.begin()->second; } -int ChildViewSpacing::GetTrailingInset() const { +int FlexLayout::ChildViewSpacing::GetTrailingInset() const { return trailing_space_; } -int ChildViewSpacing::GetLeadingSpace(size_t view_index) const { +int FlexLayout::ChildViewSpacing::GetLeadingSpace(size_t view_index) const { auto it = leading_spacings_.find(view_index); DCHECK(it != leading_spacings_.end()); return it->second; } -int ChildViewSpacing::GetTotalSpace() const { +int FlexLayout::ChildViewSpacing::GetTotalSpace() const { return std::accumulate( leading_spacings_.cbegin(), leading_spacings_.cend(), trailing_space_, [](int total, const auto& value) { return total + value.second; }); } -int ChildViewSpacing::GetAddDelta(size_t view_index) const { +int FlexLayout::ChildViewSpacing::GetAddDelta(size_t view_index) const { DCHECK(!HasViewIndex(view_index)); base::Optional<size_t> prev = GetPreviousViewIndex(view_index); base::Optional<size_t> next = GetNextViewIndex(view_index); @@ -253,9 +177,9 @@ return new_spacing - old_spacing; } -void ChildViewSpacing::AddViewIndex(size_t view_index, - int* new_leading, - int* new_trailing) { +void FlexLayout::ChildViewSpacing::AddViewIndex(size_t view_index, + int* new_leading, + int* new_trailing) { DCHECK(!HasViewIndex(view_index)); base::Optional<size_t> prev = GetPreviousViewIndex(view_index); base::Optional<size_t> next = GetNextViewIndex(view_index); @@ -274,7 +198,7 @@ *new_trailing = trailing_space; } -base::Optional<size_t> ChildViewSpacing::GetPreviousViewIndex( +base::Optional<size_t> FlexLayout::ChildViewSpacing::GetPreviousViewIndex( size_t view_index) const { const auto it = leading_spacings_.lower_bound(view_index); if (it == leading_spacings_.begin()) @@ -282,7 +206,7 @@ return std::prev(it)->first; } -base::Optional<size_t> ChildViewSpacing::GetNextViewIndex( +base::Optional<size_t> FlexLayout::ChildViewSpacing::GetNextViewIndex( size_t view_index) const { const auto it = leading_spacings_.upper_bound(view_index); if (it == leading_spacings_.end()) @@ -290,144 +214,206 @@ return it->first; } -// Holds child-view-specific layout parameters that are not stored in the -// properties system. -// -// We should consider storing some or all of these in the properites system. -struct ChildLayoutParams { - bool excluded = false; - bool hidden_by_owner = false; -}; +// Represents a specific stored layout given a set of size bounds. +struct FlexLayout::FlexLayoutData { + FlexLayoutData() {} + ~FlexLayoutData() = default; -// Internal data structure and functionality for FlexLayout so we don't have to -// declare a bunch of classes and data in the .h file. -class FlexLayoutInternal { - public: - explicit FlexLayoutInternal(FlexLayout* layout) - : layout_(*layout) {} //, cached_layouts_(kMaxCachedLayouts) {} + size_t num_children() const { return child_data.size(); } - // Suggests that the current layout needs to be recalculated. Setting |force| - // to true indicates that we know all of the cached layouts are invalid and we - // should discard them; otherwise we will keep them and re-validate on the - // next layout pass. - void InvalidateLayout(bool force); + ProposedLayout layout; - // Gets the proposed layout for a set of size bounds. Returns a cached layout - // if one is present and valid. - const Layout& CalculateLayout(const SizeBounds& bounds); + // Holds additional information about the child views of this layout. + std::vector<FlexChildData> child_data; - // Applies an existing layout to all child views, with the appropriate - // current alignment. - void DoLayout(const Layout& layout, const gfx::Rect& bounds); + // The total size of the layout (minus parent insets). + NormalizedSize total_size; + NormalizedInsets interior_margin; + gfx::Insets host_insets; private: - // Maps a flex order (lower = allocated first, and therefore higher priority) - // to the indices of child views within that order that can flex. - // See FlexSpecification::order(). - using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>; - - LayoutOrientation orientation() const { return layout_.orientation(); } - - // Determines whether a layout is still valid. - bool IsLayoutValid(const Layout& cached_layout) const; - - // Creates a brand new layout from the available |bounds|. - // Call DoLayout() to actually apply the layout. - const Layout& CalculateNewLayout(const NormalizedSizeBounds& bounds); - - // Applies flex rules to each view in a layout, updating |layout| and - // |child_spacing|. - // - // If |expandable_views| is specified, any view requesting more than its - // preferred size will be clamped to its preferred size and be added to - // |expandable_views| for later processing after all other flex space has been - // allocated. - // - // Typically, this method will be called once with |expandable_views| set and - // then again with it null to allocate the remaining space. - void AllocateFlexSpace( - Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds, - const FlexOrderToViewIndexMap& order_to_index, - FlexOrderToViewIndexMap* expandable_views = nullptr) const; - - // Calculates the position of each child view and the size of the overall - // layout based on tentative visibilities and sizes for each child. - void UpdateLayoutFromChildren(Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds) const; - - // Calculates the cross-layout space available to a view based on the - // available space and margins. - base::Optional<int> GetAvailableCrossAxisSize( - const Layout& layout, - size_t child_index, - const NormalizedSizeBounds& bounds) const; - - // Calculates a margin between two child views based on each's margin and any - // internal padding present in one or both elements. Uses properties of the - // layout, like whether adjacent margins should be collapsed. - int CalculateMargin(int margin1, int margin2, int internal_padding) const; - - // Calculates the preferred spacing between two child views, or between a - // view edge and the first or last visible child views. - int CalculateChildSpacing(const Layout& layout, - base::Optional<size_t> child1_index, - base::Optional<size_t> child2_index) const; - - // Fetches the layout-specific value for a child view, then the layout-default - // value, then the global default value if none of the others are present. - template <typename T> - T GetViewProperty(const View* view, - const ui::ClassProperty<T*>* property) const; - - FlexLayout& layout_; - - // Instead of marking each layout as dirty/needing validation, we instead keep - // a value that changes every time InvalidateLayout() is called. If the value - // stored in a cached layout doesn't match this value, we validate it and - // update the value. We use an int64_t because the odds of spurious collision - // (i.e. the counter wrapping back around to the *exact* same value before - // validating) are effectively zero. - uint32_t layout_counter_ = 0; - LayoutCache layout_cache_; - - DISALLOW_COPY_AND_ASSIGN(FlexLayoutInternal); + DISALLOW_COPY_AND_ASSIGN(FlexLayoutData); }; -void FlexLayoutInternal::InvalidateLayout(bool force) { - ++layout_counter_; - if (force) - layout_cache_.Clear(); +FlexLayout::PropertyHandler::PropertyHandler(FlexLayout* layout) + : layout_(layout) {} + +void FlexLayout::PropertyHandler::AfterPropertyChange(const void* key, + int64_t old_value) { + layout_->InvalidateLayout(); } -const Layout& FlexLayoutInternal::CalculateLayout(const SizeBounds& bounds) { - // If bounds are smaller than the minimum cross axis size, expand them. - NormalizedSizeBounds normalized_bounds = Normalize(orientation(), bounds); - if (normalized_bounds.cross() && - *normalized_bounds.cross() < layout_.minimum_cross_axis_size()) { - normalized_bounds = NormalizedSizeBounds{normalized_bounds.main(), - layout_.minimum_cross_axis_size()}; +// FlexLayout +// ------------------------------------------------------------------- + +FlexLayout::FlexLayout() {} +FlexLayout::~FlexLayout() = default; + +FlexLayout& FlexLayout::SetOrientation(LayoutOrientation orientation) { + if (orientation != orientation_) { + orientation_ = orientation; + InvalidateLayout(); + } + return *this; +} + +FlexLayout& FlexLayout::SetCollapseMargins(bool collapse_margins) { + if (collapse_margins != collapse_margins_) { + collapse_margins_ = collapse_margins; + InvalidateLayout(); + } + return *this; +} + +FlexLayout& FlexLayout::SetMainAxisAlignment( + LayoutAlignment main_axis_alignment) { + DCHECK_NE(main_axis_alignment, LayoutAlignment::kStretch) + << "Main axis stretch/justify is not yet supported."; + if (main_axis_alignment_ != main_axis_alignment) { + main_axis_alignment_ = main_axis_alignment; + InvalidateLayout(); + } + return *this; +} + +FlexLayout& FlexLayout::SetCrossAxisAlignment( + LayoutAlignment cross_axis_alignment) { + if (cross_axis_alignment_ != cross_axis_alignment) { + cross_axis_alignment_ = cross_axis_alignment; + InvalidateLayout(); + } + return *this; +} + +FlexLayout& FlexLayout::SetInteriorMargin(const gfx::Insets& interior_margin) { + if (interior_margin_ != interior_margin) { + interior_margin_ = interior_margin; + InvalidateLayout(); + } + return *this; +} + +FlexLayout& FlexLayout::SetMinimumCrossAxisSize(int size) { + if (minimum_cross_axis_size_ != size) { + minimum_cross_axis_size_ = size; + InvalidateLayout(); + } + return *this; +} + +LayoutManagerBase::ProposedLayout FlexLayout::CalculateProposedLayout( + const SizeBounds& size_bounds) const { + FlexLayoutData data; + + data.host_insets = host_view()->GetInsets(); + data.interior_margin = Normalize(orientation(), interior_margin()); + NormalizedSizeBounds bounds = Normalize(orientation(), size_bounds); + bounds.Inset(Normalize(orientation(), data.host_insets)); + if (bounds.cross() && *bounds.cross() < minimum_cross_axis_size()) + bounds.set_cross(minimum_cross_axis_size()); + + // Populate the child layout data vectors and the order-to-index map. + FlexOrderToViewIndexMap order_to_view_index; + InitializeChildData(bounds, &data, &order_to_view_index); + + // Do the initial layout update, calculating spacing between children. + ChildViewSpacing child_spacing( + base::BindRepeating(&FlexLayout::CalculateChildSpacing, + base::Unretained(this), std::cref(data))); + UpdateLayoutFromChildren(bounds, &data, &child_spacing); + + if (bounds.main().has_value() && !order_to_view_index.empty()) { + // Flex up to preferred size. + FlexOrderToViewIndexMap expandable_views; + AllocateFlexSpace(bounds, order_to_view_index, &data, &child_spacing, + &expandable_views); + + // Flex views that can exceed their preferred size. + if (!expandable_views.empty()) + AllocateFlexSpace(bounds, expandable_views, &data, &child_spacing); } - // See if we have a cached layout already that is still valid. - Layout* const cached_layout = layout_cache_.Get(normalized_bounds); - if (cached_layout && IsLayoutValid(*cached_layout)) - return *cached_layout; + // Calculate the size of the host view. + data.layout.host_size = Denormalize(orientation(), data.total_size); + data.layout.host_size.Enlarge(data.host_insets.width(), + data.host_insets.height()); - // Calculate a new layout from scratch. - return CalculateNewLayout(normalized_bounds); + // Size and position the children in screen space. + CalculateChildBounds(size_bounds, &data); + + return data.layout; } -void FlexLayoutInternal::DoLayout(const Layout& layout, - const gfx::Rect& bounds) { - NormalizedPoint start = Normalize(orientation(), bounds.origin()); +void FlexLayout::InitializeChildData( + const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + FlexOrderToViewIndexMap* flex_order_to_index) const { + // Step through the children, creating placeholder layout view elements + // and setting up initial minimal visibility. + const bool main_axis_bounded = bounds.main().has_value(); + for (auto it = host_view()->children().begin(); + it != host_view()->children().end(); ++it) { + View* const child = *it; + if (!IsChildIncludedInLayout(child)) + continue; + const size_t view_index = data->num_children(); + data->layout.child_layouts.emplace_back(ChildLayout{child}); + ChildLayout& child_layout = data->layout.child_layouts.back(); + data->child_data.emplace_back( + GetViewProperty(child, layout_defaults_, views::kFlexBehaviorKey)); + FlexChildData& flex_child = data->child_data.back(); - // Apply main axis alignment. - const int excess_main = - Normalize(orientation(), bounds.size()).main() - layout.total_size.main(); - switch (layout_.main_axis_alignment()) { + flex_child.margins = + Normalize(orientation(), + GetViewProperty(child, layout_defaults_, views::kMarginsKey)); + flex_child.internal_padding = Normalize( + orientation(), + GetViewProperty(child, layout_defaults_, views::kInternalPaddingKey)); + flex_child.preferred_size = + Normalize(orientation(), child->GetPreferredSize()); + + // gfx::Size calculation depends on whether flex is allowed. + if (main_axis_bounded) { + flex_child.available_size = { + 0, GetAvailableCrossAxisSize(*data, view_index, bounds)}; + flex_child.current_size = Normalize( + orientation(), + flex_child.flex.rule().Run( + child, Denormalize(orientation(), flex_child.available_size))); + + // We should revisit whether this is a valid assumption for text views + // in vertical layouts. + DCHECK_GE(flex_child.preferred_size.main(), + flex_child.current_size.main()); + + // Keep track of non-hidden flex controls. + const bool can_flex = + (flex_child.flex.weight() > 0 && + flex_child.preferred_size.main() > 0) || + flex_child.current_size.main() < flex_child.preferred_size.main(); + if (can_flex) + (*flex_order_to_index)[flex_child.flex.order()].push_back(view_index); + + } else { + // All non-flex or unbounded controls get preferred size. + flex_child.current_size = flex_child.preferred_size; + } + + child_layout.visible = flex_child.current_size.main() > 0; + } +} + +void FlexLayout::CalculateChildBounds(const SizeBounds& size_bounds, + FlexLayoutData* data) const { + // Apply main axis alignment (we've already done cross-axis alignment above). + int available_main = (size_bounds.width() ? *size_bounds.width() + : data->layout.host_size.width()); + available_main = std::max(0, available_main - data->host_insets.width()); + const int excess_main = available_main - data->total_size.main(); + NormalizedPoint start = + Normalize(orientation(), + gfx::Point(data->host_insets.left(), data->host_insets.top())); + switch (main_axis_alignment()) { case LayoutAlignment::kStart: break; case LayoutAlignment::kCenter: @@ -441,29 +427,34 @@ break; } - // Position controls within the client area. - for (const ChildLayout& child_layout : layout.child_layouts) { - if (child_layout.excluded) - continue; - if (child_layout.visible != child_layout.view->GetVisible()) - layout_.SetViewVisibility(child_layout.view, child_layout.visible); + // Calculate the actual child bounds. + for (size_t i = 0; i < data->num_children(); ++i) { + ChildLayout& child_layout = data->layout.child_layouts[i]; if (child_layout.visible) { - NormalizedRect actual = child_layout.actual_bounds; + FlexChildData& flex_child = data->child_data[i]; + NormalizedRect actual = flex_child.actual_bounds; actual.Offset(start.main(), start.cross()); - gfx::Rect denormalized = Denormalize(orientation(), actual); - child_layout.view->SetBoundsRect(denormalized); + child_layout.bounds = Denormalize(orientation(), actual); } } } -base::Optional<int> FlexLayoutInternal::GetAvailableCrossAxisSize( - const Layout& layout, +int FlexLayout::CalculateMargin(int margin1, + int margin2, + int internal_padding) const { + const int result = + collapse_margins() ? std::max(margin1, margin2) : margin1 + margin2; + return std::max(0, result - internal_padding); +} + +base::Optional<int> FlexLayout::GetAvailableCrossAxisSize( + const FlexLayoutData& layout, size_t child_index, const NormalizedSizeBounds& bounds) const { if (!bounds.cross()) return base::nullopt; - const ChildLayout& child_layout = layout.child_layouts[child_index]; + const FlexChildData& child_layout = layout.child_data[child_index]; const int leading_margin = CalculateMargin(layout.interior_margin.cross_leading(), child_layout.margins.cross_leading(), @@ -475,79 +466,71 @@ return std::max(0, *bounds.cross() - (leading_margin + trailing_margin)); } -int FlexLayoutInternal::CalculateMargin(int margin1, - int margin2, - int internal_padding) const { - const int result = layout_.collapse_margins() ? std::max(margin1, margin2) - : margin1 + margin2; - return std::max(0, result - internal_padding); -} - -int FlexLayoutInternal::CalculateChildSpacing( - const Layout& layout, +int FlexLayout::CalculateChildSpacing( + const FlexLayoutData& layout, base::Optional<size_t> child1_index, base::Optional<size_t> child2_index) const { const int left_margin = - child1_index ? layout.child_layouts[*child1_index].margins.main_trailing() + child1_index ? layout.child_data[*child1_index].margins.main_trailing() : layout.interior_margin.main_leading(); const int right_margin = - child2_index ? layout.child_layouts[*child2_index].margins.main_leading() + child2_index ? layout.child_data[*child2_index].margins.main_leading() : layout.interior_margin.main_trailing(); const int left_padding = child1_index - ? layout.child_layouts[*child1_index].internal_padding.main_trailing() + ? layout.child_data[*child1_index].internal_padding.main_trailing() : 0; const int right_padding = child2_index - ? layout.child_layouts[*child2_index].internal_padding.main_leading() + ? layout.child_data[*child2_index].internal_padding.main_leading() : 0; return CalculateMargin(left_margin, right_margin, left_padding + right_padding); } -void FlexLayoutInternal::UpdateLayoutFromChildren( - Layout* layout, - ChildViewSpacing* child_spacing, - const NormalizedSizeBounds& bounds) const { +void FlexLayout::UpdateLayoutFromChildren( + const NormalizedSizeBounds& bounds, + FlexLayoutData* data, + ChildViewSpacing* child_spacing) const { // Calculate starting minimum for cross-axis size. int min_cross_size = - std::max(layout_.minimum_cross_axis_size(), - CalculateMargin(layout->interior_margin.cross_leading(), - layout->interior_margin.cross_trailing(), 0)); - layout->total_size = NormalizedSize(0, min_cross_size); + std::max(minimum_cross_axis_size(), + CalculateMargin(data->interior_margin.cross_leading(), + data->interior_margin.cross_trailing(), 0)); + data->total_size = NormalizedSize(0, min_cross_size); // For cases with a non-zero cross-axis bound, the objective is to fit the // layout into that precise size, not to determine what size we need. bool force_cross_size = false; if (bounds.cross() && *bounds.cross() > 0) { - layout->total_size.SetToMax(0, *bounds.cross()); + data->total_size.SetToMax(0, *bounds.cross()); force_cross_size = true; } - std::vector<Inset1D> cross_spacings(layout->child_layouts.size()); - for (size_t i = 0; i < layout->child_layouts.size(); ++i) { - ChildLayout& child_layout = layout->child_layouts[i]; + std::vector<Inset1D> cross_spacings(data->num_children()); + for (size_t i = 0; i < data->num_children(); ++i) { + FlexChildData& flex_child = data->child_data[i]; - // We don't have to deal with excluded or invisible children. - if (child_layout.excluded || !child_layout.visible) + // We don't have to deal with invisible children. + if (!data->layout.child_layouts[i].visible) continue; // Update the cross-axis margins and if necessary, the size. Inset1D& cross_spacing = cross_spacings[i]; cross_spacing.set_leading( - CalculateMargin(layout->interior_margin.cross_leading(), - child_layout.margins.cross_leading(), - child_layout.internal_padding.cross_leading())); + CalculateMargin(data->interior_margin.cross_leading(), + flex_child.margins.cross_leading(), + flex_child.internal_padding.cross_leading())); cross_spacing.set_trailing( - CalculateMargin(layout->interior_margin.cross_trailing(), - child_layout.margins.cross_trailing(), - child_layout.internal_padding.cross_trailing())); + CalculateMargin(data->interior_margin.cross_trailing(), + flex_child.margins.cross_trailing(), + flex_child.internal_padding.cross_trailing())); if (!force_cross_size) { - const int cross_size = std::min(child_layout.current_size.cross(), - child_layout.preferred_size.cross()); - layout->total_size.SetToMax(0, cross_spacing.size() + cross_size); + const int cross_size = std::min(flex_child.current_size.cross(), + flex_child.preferred_size.cross()); + data->total_size.SetToMax(0, cross_spacing.size() + cross_size); } // Calculate main-axis size and upper-left main axis coordinate. @@ -556,50 +539,46 @@ leading_space = child_spacing->GetLeadingSpace(i); else child_spacing->AddViewIndex(i, &leading_space); - layout->total_size.Enlarge(leading_space, 0); + data->total_size.Enlarge(leading_space, 0); - const int size_main = child_layout.current_size.main(); - child_layout.actual_bounds.set_origin_main(layout->total_size.main()); - child_layout.actual_bounds.set_size_main(size_main); - layout->total_size.Enlarge(size_main, 0); + const int size_main = flex_child.current_size.main(); + flex_child.actual_bounds.set_origin_main(data->total_size.main()); + flex_child.actual_bounds.set_size_main(size_main); + data->total_size.Enlarge(size_main, 0); } // Add the end margin. - layout->total_size.Enlarge(child_spacing->GetTrailingInset(), 0); + data->total_size.Enlarge(child_spacing->GetTrailingInset(), 0); // Calculate cross-axis positioning based on the cross margins and size that // were calculated above. - const Span cross_span(0, layout->total_size.cross()); - for (size_t i = 0; i < layout->child_layouts.size(); ++i) { - ChildLayout& child_layout = layout->child_layouts[i]; - - // We don't have to deal with excluded or invisible children. - if (child_layout.excluded || !child_layout.visible) - continue; + const Span cross_span(0, data->total_size.cross()); + for (size_t i = 0; i < data->num_children(); ++i) { + FlexChildData& flex_child = data->child_data[i]; // Start with a size appropriate for the child view. For child views which // can become larger than the preferred size, start with the preferred size // and let the alignment operation (specifically, if the alignment is set to // kStretch) grow the child view. - const int starting_cross_size = std::min( - child_layout.current_size.cross(), child_layout.preferred_size.cross()); - child_layout.actual_bounds.set_size_cross(starting_cross_size); - child_layout.actual_bounds.AlignCross( - cross_span, layout_.cross_axis_alignment(), cross_spacings[i]); + const int starting_cross_size = std::min(flex_child.current_size.cross(), + flex_child.preferred_size.cross()); + flex_child.actual_bounds.set_size_cross(starting_cross_size); + flex_child.actual_bounds.AlignCross(cross_span, cross_axis_alignment(), + cross_spacings[i]); } } -void FlexLayoutInternal::AllocateFlexSpace( - Layout* layout, - ChildViewSpacing* child_spacing, +void FlexLayout::AllocateFlexSpace( const NormalizedSizeBounds& bounds, const FlexOrderToViewIndexMap& order_to_index, + FlexLayoutData* data, + ChildViewSpacing* child_spacing, FlexOrderToViewIndexMap* expandable_views) const { // Step through each flex priority allocating as much remaining space as // possible to each flex view. for (const auto& flex_elem : order_to_index) { // Check to see we haven't filled available space. - int remaining = *bounds.main() - layout->total_size.main(); + int remaining = *bounds.main() - data->total_size.main(); if (remaining <= 0) break; const int flex_order = flex_elem.first; @@ -629,11 +608,11 @@ // reasonable margins and by using flex order). // Flex children at this priority order. - int flex_total = std::accumulate( - flex_elem.second.begin(), flex_elem.second.end(), 0, - [layout](int total, size_t index) { - return total + layout->child_layouts[index].flex.weight(); - }); + int flex_total = + std::accumulate(flex_elem.second.begin(), flex_elem.second.end(), 0, + [data](int total, size_t index) { + return total + data->child_data[index].flex.weight(); + }); // Note: because the child views are evaluated in order, if preferred // minimum sizes are not consistent across a single priority expanding @@ -645,12 +624,13 @@ remaining >= 0 && index_it != flex_elem.second.end(); ++index_it) { const size_t view_index = *index_it; - ChildLayout& child_layout = layout->child_layouts[view_index]; + ChildLayout& child_layout = data->layout.child_layouts[view_index]; + FlexChildData& flex_child = data->child_data[view_index]; // Offer a share of the remaining space to the view. int flex_amount; - if (child_layout.flex.weight() > 0) { - const int flex_weight = child_layout.flex.weight(); + if (flex_child.flex.weight() > 0) { + const int flex_weight = flex_child.flex.weight(); // Round up so we give slightly greater weight to earlier views. flex_amount = int{std::ceil((float{remaining} * flex_weight) / flex_total)}; @@ -680,17 +660,17 @@ // view since it is considered a fixed overhead of the layout if it is // nonzero. const int old_size = - child_layout.visible ? child_layout.current_size.main() : 0; + child_layout.visible ? flex_child.current_size.main() : 0; // Offer the modified flex space to the child view and see how large it // wants to be (or if it wants to be visible at that size at all). const NormalizedSizeBounds available( flex_amount + old_size - margin_delta, - child_layout.available_size.cross()); + flex_child.available_size.cross()); NormalizedSize new_size = Normalize( orientation(), - child_layout.flex.rule().Run(child_layout.view, - Denormalize(orientation(), available))); + flex_child.flex.rule().Run(child_layout.child_view, + Denormalize(orientation(), available))); if (new_size.main() <= 0) continue; @@ -699,9 +679,9 @@ // them to |expandable_views| so that the remaining space can be allocated // later. if (expandable_views && - new_size.main() >= child_layout.preferred_size.main()) { + new_size.main() >= flex_child.preferred_size.main()) { (*expandable_views)[flex_order].push_back(view_index); - new_size.set_main(child_layout.preferred_size.main()); + new_size.set_main(flex_child.preferred_size.main()); } // If the amount of space claimed increases (but is still within @@ -710,8 +690,8 @@ const int to_deduct = (new_size.main() - old_size) + margin_delta; DCHECK_GE(to_deduct, 0); if (to_deduct > 0 && to_deduct <= remaining) { - child_layout.available_size = available; - child_layout.current_size = new_size; + flex_child.available_size = available; + flex_child.current_size = new_size; child_layout.visible = true; remaining -= to_deduct; if (!child_spacing->HasViewIndex(view_index)) @@ -723,367 +703,8 @@ // Reposition the child controls (taking margins into account) and // calculate remaining space. if (dirty) - UpdateLayoutFromChildren(layout, child_spacing, bounds); + UpdateLayoutFromChildren(bounds, data, child_spacing); } } -const Layout& FlexLayoutInternal::CalculateNewLayout( - const NormalizedSizeBounds& bounds) { - DCHECK(!bounds.cross() || - *bounds.cross() >= layout_.minimum_cross_axis_size()); - std::unique_ptr<Layout> layout = std::make_unique<Layout>(layout_counter_); - layout->interior_margin = Normalize(orientation(), layout_.interior_margin()); - FlexOrderToViewIndexMap order_to_view_index; - const bool main_axis_bounded = bounds.main().has_value(); - - // Step through the children, creating placeholder layout view elements - // and setting up initial minimal visibility. - View* const view = layout_.host(); - for (size_t i = 0; i < view->children().size(); ++i) { - View* child = view->children()[i]; - layout->child_layouts.emplace_back( - child, GetViewProperty(child, views::kFlexBehaviorKey)); - ChildLayout& child_layout = layout->child_layouts.back(); - - child_layout.excluded = layout_.IsViewExcluded(child); - if (child_layout.excluded) - continue; - - child_layout.margins = - Normalize(orientation(), GetViewProperty(child, views::kMarginsKey)); - child_layout.internal_padding = Normalize( - orientation(), GetViewProperty(child, views::kInternalPaddingKey)); - child_layout.preferred_size = - Normalize(orientation(), child->GetPreferredSize()); - - child_layout.hidden_by_owner = layout_.IsHiddenByOwner(child); - child_layout.visible = !child_layout.hidden_by_owner; - if (child_layout.hidden_by_owner) - continue; - - // gfx::Size calculation depends on whether flex is allowed. - if (main_axis_bounded) { - child_layout.available_size = { - 0, GetAvailableCrossAxisSize(*layout, i, bounds)}; - child_layout.current_size = Normalize( - orientation(), - child_layout.flex.rule().Run( - child, Denormalize(orientation(), child_layout.available_size))); - - // We should revisit whether this is a valid assumption for text views - // in vertical layouts. - DCHECK_GE(child_layout.preferred_size.main(), - child_layout.current_size.main()); - - // Keep track of non-hidden flex controls. - const bool can_flex = - (child_layout.flex.weight() > 0 && - child_layout.preferred_size.main() > 0) || - child_layout.current_size.main() < child_layout.preferred_size.main(); - if (can_flex) - order_to_view_index[child_layout.flex.order()].push_back(i); - - } else { - // All non-flex or unbounded controls get preferred size. - child_layout.current_size = child_layout.preferred_size; - } - - child_layout.visible = child_layout.current_size.main() > 0; - - // Actual size and positioning will be set during the final layout - // calculation. - } - - // Do the initial layout update, calculating spacing between children. - ChildViewSpacing child_spacing( - base::BindRepeating(&FlexLayoutInternal::CalculateChildSpacing, - base::Unretained(this), std::cref(*layout))); - UpdateLayoutFromChildren(layout.get(), &child_spacing, bounds); - - if (main_axis_bounded && !order_to_view_index.empty()) { - // Flex up to preferred size. - FlexOrderToViewIndexMap expandable_views; - AllocateFlexSpace(layout.get(), &child_spacing, bounds, order_to_view_index, - &expandable_views); - - // Flex views that can exceed their preferred size. - if (!expandable_views.empty()) - AllocateFlexSpace(layout.get(), &child_spacing, bounds, expandable_views); - } - - const Layout& result = *layout; - layout_cache_.Put(bounds, std::move(layout)); - return result; -} - -bool FlexLayoutInternal::IsLayoutValid(const Layout& cached_layout) const { - // If we've already evaluated a layout for this size and not been - // invalidated since then, then this is a valid layout. - if (cached_layout.layout_id == layout_counter_) - return true; - - // Need to compare preferred child sizes with what we're seeing. - View* const view = layout_.host(); - auto iter = view->children().cbegin(); - for (const ChildLayout& proposed_view_layout : cached_layout.child_layouts) { - // Check that there is another child and that it's the view we expect. - DCHECK(iter != view->children().cend()) - << "Child views should not be removed without clearing the cache."; - - const View* child = *iter++; - - // Ensure child views have not been reordered. - if (child != proposed_view_layout.view) - return false; - - // Ignore hidden and excluded views, unless their status has changed. - const bool excluded = layout_.IsViewExcluded(child); - if (proposed_view_layout.excluded != excluded) - return false; - if (excluded) - continue; - - const bool hidden_by_owner = layout_.IsHiddenByOwner(child); - if (proposed_view_layout.hidden_by_owner != hidden_by_owner) - return false; - if (hidden_by_owner) - continue; - - // Sanity check that a child's visibility hasn't been modified outside - // the layout manager. - if (proposed_view_layout.visible != child->GetVisible()) - return false; - - if (proposed_view_layout.visible) { - // Check that view margins haven't changed for visible controls. - if (GetViewProperty(child, views::kMarginsKey) != - Denormalize(orientation(), proposed_view_layout.margins)) - return false; - - // Same for internal padding. - if (GetViewProperty(child, views::kInternalPaddingKey) != - Denormalize(orientation(), proposed_view_layout.internal_padding)) - return false; - } - - // Check that the control still has the same preferred size given its - // flex and visibility. - if (child->GetPreferredSize() != - Denormalize(orientation(), proposed_view_layout.preferred_size)) - return false; - - const FlexSpecification child_flex = - GetViewProperty(child, views::kFlexBehaviorKey); - const gfx::Size preferred = child_flex.rule().Run( - child, Denormalize(orientation(), proposed_view_layout.available_size)); - if (preferred != - Denormalize(orientation(), proposed_view_layout.current_size)) - return false; - } - - DCHECK(iter == view->children().cend()) - << "Child views should not be added without clearing the cache."; - - // This layout is still valid. Update the layout counter to show it's valid - // in the current layout context. - cached_layout.layout_id = layout_counter_; - return true; -} - -template <typename T> -T FlexLayoutInternal::GetViewProperty( - const View* view, - const ui::ClassProperty<T*>* property) const { - T* found_value = view->GetProperty(property); - if (found_value) - return *found_value; - found_value = layout_.layout_defaults_.GetProperty(property); - if (found_value) - return *found_value; - return T(); -} - -} // namespace internal - -// FlexLayout -// ------------------------------------------------------------------- - -FlexLayout::PropertyHandler::PropertyHandler( - internal::FlexLayoutInternal* layout) - : layout_(layout) {} - -void FlexLayout::PropertyHandler::AfterPropertyChange(const void* key, - int64_t old_value) { - layout_->InvalidateLayout(false); -} - -FlexLayout::FlexLayout() - : internal_(std::make_unique<internal::FlexLayoutInternal>(this)), - layout_defaults_(internal_.get()) {} - -FlexLayout::~FlexLayout() = default; - -FlexLayout& FlexLayout::SetOrientation(LayoutOrientation orientation) { - if (orientation != orientation_) { - orientation_ = orientation; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetCollapseMargins(bool collapse_margins) { - if (collapse_margins != collapse_margins_) { - collapse_margins_ = collapse_margins; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetMainAxisAlignment( - LayoutAlignment main_axis_alignment) { - DCHECK_NE(main_axis_alignment, LayoutAlignment::kStretch) - << "Main axis stretch/justify is not yet supported."; - if (main_axis_alignment_ != main_axis_alignment) { - main_axis_alignment_ = main_axis_alignment; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetCrossAxisAlignment( - LayoutAlignment cross_axis_alignment) { - if (cross_axis_alignment_ != cross_axis_alignment) { - cross_axis_alignment_ = cross_axis_alignment; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetInteriorMargin(const gfx::Insets& interior_margin) { - if (interior_margin_ != interior_margin) { - interior_margin_ = interior_margin; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetMinimumCrossAxisSize(int size) { - if (minimum_cross_axis_size_ != size) { - minimum_cross_axis_size_ = size; - internal_->InvalidateLayout(true); - } - return *this; -} - -FlexLayout& FlexLayout::SetViewExcluded(const View* view, bool excluded) { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - if (excluded != it->second.excluded) { - it->second.excluded = excluded; - InvalidateLayout(); - } - return *this; -} - -bool FlexLayout::IsViewExcluded(const View* view) const { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - return it->second.excluded; -} - -bool FlexLayout::IsHiddenByOwner(const View* view) const { - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - return it->second.hidden_by_owner; -} - -gfx::Size FlexLayout::GetPreferredSize(const View* host) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds()); -} - -int FlexLayout::GetPreferredHeightForWidth(const View* host, int width) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds{width, base::nullopt}).height(); -} - -gfx::Size FlexLayout::GetMinimumSize(const View* host) const { - DCHECK_EQ(host_, host); - return GetPreferredSize(SizeBounds{0, 0}); -} - -void FlexLayout::InvalidateLayout() { - internal_->InvalidateLayout(false); -} - -void FlexLayout::Installed(View* host) { - DCHECK(!host_); - host_ = host; - // Add all the existing children when the layout manager is installed. - // If new children are added, ViewAdded() will be called and we'll add data - // there. - for (View* child : host->children()) { - internal::ChildLayoutParams child_layout_params; - child_layout_params.hidden_by_owner = !child->GetVisible(); - child_params_.emplace(child, child_layout_params); - } -} - -void FlexLayout::ViewAdded(View* host, View* view) { - DCHECK_EQ(host_, host); - internal::ChildLayoutParams child_layout_params; - child_layout_params.hidden_by_owner = !view->GetVisible(); - child_params_.emplace(view, child_layout_params); - internal_->InvalidateLayout(true); -} - -void FlexLayout::ViewRemoved(View* host, View* view) { - child_params_.erase(view); - internal_->InvalidateLayout(true); -} - -void FlexLayout::ViewVisibilitySet(View* host, View* view, bool visible) { - DCHECK_EQ(host, host_); - DCHECK_EQ(view->parent(), host); - auto it = child_params_.find(view); - DCHECK(it != child_params_.end()); - const bool hide = !visible; - if (it->second.hidden_by_owner != hide) { - it->second.hidden_by_owner = hide; - if (!it->second.excluded) { - // It's not always obvious to our host that an owner of a child view - // changing its visibility could change a layout. So we'll notify the host - // view that its layout is potentially invalid, and it will in turn call - // InvalidateLayout() on the layout manager (i.e. this object). - host_->InvalidateLayout(); - } - } -} - -// Retrieve the preferred size for the control in the given bounds. -gfx::Size FlexLayout::GetPreferredSize(const SizeBounds& bounds) const { - if (!host_) - return gfx::Size(); - - const gfx::Insets insets = host_->GetInsets(); - SizeBounds content_bounds = bounds; - content_bounds.Enlarge(-insets.width(), -insets.height()); - const internal::Layout& proposed_layout = - internal_->CalculateLayout(content_bounds); - gfx::Size result = Denormalize(orientation(), proposed_layout.total_size); - result.Enlarge(insets.width(), insets.height()); - return result; -} - -void FlexLayout::Layout(View* host) { - DCHECK_EQ(host, host_); - // TODO(dfried): for a handful of views which override GetInsets(), the way - // this method is implemented in View may be incorrect. Please revisit. - gfx::Rect bounds = host_->GetContentsBounds(); - const internal::Layout& layout = - internal_->CalculateLayout(SizeBounds(bounds.size())); - - internal_->DoLayout(layout, bounds); -} - } // namespace views
diff --git a/ui/views/layout/flex_layout.h b/ui/views/layout/flex_layout.h index 16649b2..5ca0ab23 100644 --- a/ui/views/layout/flex_layout.h +++ b/ui/views/layout/flex_layout.h
@@ -9,6 +9,7 @@ #include <memory> #include <set> #include <string> +#include <vector> #include "base/compiler_specific.h" #include "base/macros.h" @@ -16,22 +17,16 @@ #include "ui/base/class_property.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/layout/flex_layout_types.h" -#include "ui/views/layout/layout_manager.h" - -namespace gfx { -class Size; -} // namespace gfx +#include "ui/views/layout/layout_manager_base.h" +#include "ui/views/views_export.h" namespace views { class View; namespace internal { -struct ChildLayoutParams; -class FlexLayoutInternal; -} // namespace internal - -class View; +class NormalizedSizeBounds; +} // Provides CSS-like layout for a one-dimensional (vertical or horizontal) // arrangement of child views. Independent alignment can be specified for the @@ -72,11 +67,11 @@ // take up its preferred size in the layout. // // The core function of this class is contained in -// GetPreferredSize(maximum_size) and Layout(host). In both cases, a layout will +// GetPreferredSize(maximum_size) and Layout(). In both cases, a layout will // be cached and typically not recalculated as long as none of the layout's // properties or the preferred size or visibility of any of its children has // changed. -class VIEWS_EXPORT FlexLayout : public LayoutManager { +class VIEWS_EXPORT FlexLayout : public LayoutManagerBase { public: FlexLayout(); ~FlexLayout() override; @@ -92,12 +87,6 @@ FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin); FlexLayout& SetMinimumCrossAxisSize(int size); - // Set whether a view should be excluded from the layout. Excluded views will - // be completely ignored and must be explicitly placed by the host view. - FlexLayout& SetViewExcluded(const View* view, bool excluded); - - View* host() { return host_; } - const View* host() const { return host_; } LayoutOrientation orientation() const { return orientation_; } bool collapse_margins() const { return collapse_margins_; } LayoutAlignment main_axis_alignment() const { return main_axis_alignment_; } @@ -119,39 +108,86 @@ return *this; } - bool IsViewExcluded(const View* view) const; - bool IsHiddenByOwner(const View* view) const; - - // Retrieve the preferred size for the control in the given bounds. - gfx::Size GetPreferredSize(const SizeBounds& maximum_size) const; - protected: - // views::LayoutManager: - void Installed(View* host) override; - void InvalidateLayout() override; - void ViewAdded(View* host, View* view) override; - void ViewRemoved(View* host, View* view) override; - void ViewVisibilitySet(View* host, View* view, bool visible) override; - void Layout(View* host) override; - gfx::Size GetPreferredSize(const View* host) const override; - gfx::Size GetMinimumSize(const View* host) const override; - int GetPreferredHeightForWidth(const View* host, int width) const override; + // LayoutManagerBase: + ProposedLayout CalculateProposedLayout( + const SizeBounds& size_bounds) const override; private: - friend class internal::FlexLayoutInternal; + struct ChildLayoutParams; + class ChildViewSpacing; + struct FlexLayoutData; class PropertyHandler : public ui::PropertyHandler { public: - explicit PropertyHandler(internal::FlexLayoutInternal* layout); + explicit PropertyHandler(FlexLayout* layout); protected: // ui::PropertyHandler: void AfterPropertyChange(const void* key, int64_t old_value) override; private: - internal::FlexLayoutInternal* const layout_; + FlexLayout* const layout_; }; + // Maps a flex order (lower = allocated first, and therefore higher priority) + // to the indices of child views within that order that can flex. + // See FlexSpecification::order(). + using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>; + + // Calculates a margin between two child views based on each's margin and any + // internal padding present in one or both elements. Uses properties of the + // layout, like whether adjacent margins should be collapsed. + int CalculateMargin(int margin1, int margin2, int internal_padding) const; + + // Calculates the cross-layout space available to a view based on the + // available space and margins. + base::Optional<int> GetAvailableCrossAxisSize( + const FlexLayoutData& layout, + size_t child_index, + const internal::NormalizedSizeBounds& bounds) const; + + // Calculates the preferred spacing between two child views, or between a + // view edge and the first or last visible child views. + int CalculateChildSpacing(const FlexLayoutData& layout, + base::Optional<size_t> child1_index, + base::Optional<size_t> child2_index) const; + + // Calculates the position of each child view and the size of the overall + // layout based on tentative visibilities and sizes for each child. + void UpdateLayoutFromChildren(const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + ChildViewSpacing* child_spacing) const; + + // Applies flex rules to each view in a layout, updating |data| and + // |child_spacing|. + // + // If |expandable_views| is specified, any view requesting more than its + // preferred size will be clamped to its preferred size and be added to + // |expandable_views| for later processing after all other flex space has been + // allocated. + // + // Typically, this method will be called once with |expandable_views| set and + // then again with it null to allocate the remaining space. + void AllocateFlexSpace( + const internal::NormalizedSizeBounds& bounds, + const FlexOrderToViewIndexMap& order_to_index, + FlexLayoutData* data, + ChildViewSpacing* child_spacing, + FlexOrderToViewIndexMap* expandable_views = nullptr) const; + + // Fills out the child entries for |data| and generates some initial size + // and visibility data, and stores off information about which views can + // expand in |flex_order_to_index|. + void InitializeChildData(const internal::NormalizedSizeBounds& bounds, + FlexLayoutData* data, + FlexOrderToViewIndexMap* flex_order_to_index) const; + + // Caclulates the child bounds (in screen coordinates) for each visible child + // in the layout. + void CalculateChildBounds(const SizeBounds& size_bounds, + FlexLayoutData* data) const; + // Gets the default value for a particular layout property, which will be used // if the property is not set on a child view being laid out (e.g. // kMarginsKey). @@ -186,19 +222,9 @@ // The minimum cross axis size for the layout. int minimum_cross_axis_size_ = 0; - // Tracks layout-specific information about child views. - std::map<const View*, internal::ChildLayoutParams> child_params_; - - // The view that this FlexLayout is managing the layout for. - views::View* host_ = nullptr; - - // Internal data used to cache layout information, etc. All definitions and - // data are module-private. - std::unique_ptr<internal::FlexLayoutInternal> internal_; - // Default properties for any views that don't have them explicitly set for // this layout. - PropertyHandler layout_defaults_; + PropertyHandler layout_defaults_{this}; DISALLOW_COPY_AND_ASSIGN(FlexLayout); };
diff --git a/ui/views/layout/flex_layout_types_internal.cc b/ui/views/layout/flex_layout_types_internal.cc index bb104c4..7e467f9 100644 --- a/ui/views/layout/flex_layout_types_internal.cc +++ b/ui/views/layout/flex_layout_types_internal.cc
@@ -142,6 +142,10 @@ cross_ = std::max(0, *cross_ + cross); } +void NormalizedSizeBounds::Inset(const NormalizedInsets& insets) { + Expand(-insets.main_size(), -insets.cross_size()); +} + bool NormalizedSizeBounds::operator==(const NormalizedSizeBounds& other) const { return main_ == other.main_ && cross_ == other.cross_; }
diff --git a/ui/views/layout/flex_layout_types_internal.h b/ui/views/layout/flex_layout_types_internal.h index 7a44d1f..bdefe8e 100644 --- a/ui/views/layout/flex_layout_types_internal.h +++ b/ui/views/layout/flex_layout_types_internal.h
@@ -160,6 +160,7 @@ void set_cross(const base::Optional<int>& cross) { cross_ = cross; } void Expand(int main, int cross); + void Inset(const NormalizedInsets& insets); bool operator==(const NormalizedSizeBounds& other) const; bool operator!=(const NormalizedSizeBounds& other) const;
diff --git a/ui/views/layout/flex_layout_unittest.cc b/ui/views/layout/flex_layout_unittest.cc index 9b10e13..5bda1d2c4 100644 --- a/ui/views/layout/flex_layout_unittest.cc +++ b/ui/views/layout/flex_layout_unittest.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include <algorithm> +#include <utility> #include <vector> #include "base/bind.h" @@ -315,7 +316,6 @@ View* child3 = AddChild(kChild3Size); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -324,7 +324,6 @@ // This should have no additional effect since the child is already invisible. child2->SetVisible(false); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -334,7 +333,6 @@ host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_TRUE(child2->GetVisible()); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); @@ -351,7 +349,6 @@ child2->SetVisible(false); host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -361,7 +358,6 @@ host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_TRUE(child2->GetVisible()); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); @@ -381,7 +377,6 @@ // Layout makes child view invisible due to flex rule. host_->SetSize(Size(40, 25)); host_->Layout(); - EXPECT_EQ(false, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -390,11 +385,7 @@ // Now we will make child explicitly hidden. child2->SetVisible(false); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); - - // Layout is the same, but we should report that the view is hidden by owner. host_->Layout(); - EXPECT_EQ(true, layout_->IsHiddenByOwner(child2)); EXPECT_FALSE(child2->GetVisible()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); @@ -410,20 +401,18 @@ View* child2 = AddChild(kChild2Size); const View* child3 = AddChild(kChild3Size); - layout_->SetViewExcluded(child2, true); + layout_->SetChildViewIgnoredByLayout(child2, true); child2->SetBounds(3, 3, 3, 3); host_->Layout(); - EXPECT_EQ(true, layout_->IsViewExcluded(child2)); EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Size(44, 25), host_->GetPreferredSize()); - layout_->SetViewExcluded(child2, false); + layout_->SetChildViewIgnoredByLayout(child2, false); host_->Layout(); std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), Rect(31, 5, 17, 13)}; - EXPECT_EQ(false, layout_->IsViewExcluded(child2)); EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); }
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.html b/ui/webui/resources/cr_elements/cr_input/cr_input.html index 2ec30f3e..bb39b3e 100644 --- a/ui/webui/resources/cr_elements/cr_input/cr_input.html +++ b/ui/webui/resources/cr_elements/cr_input/cr_input.html
@@ -39,9 +39,6 @@ } /* Margin between <input> and <cr-button> in the 'suffix' slot */ - /* TODO(aee): remove paper-button selector when paper-button is removed. - */ - :host ::slotted(paper-button[slot=suffix]), :host ::slotted(cr-button[slot=suffix]) { margin-inline-start: var(--cr-button-edge-spacing) !important; }
diff --git a/ui/webui/resources/cr_elements/cr_toast/cr_toast.html b/ui/webui/resources/cr_elements/cr_toast/cr_toast.html index 70daaf28..c5e9436 100644 --- a/ui/webui/resources/cr_elements/cr_toast/cr_toast.html +++ b/ui/webui/resources/cr_elements/cr_toast/cr_toast.html
@@ -54,9 +54,6 @@ color: var(--cr-toast-text-color); } - /* TODO(aee): remove paper-button selector when paper-button is removed. - */ - :host ::slotted(paper-button), :host ::slotted(cr-button) { background-color: transparent !important; border: none !important; @@ -66,7 +63,6 @@ padding: 8px !important; } - :host ::slotted(paper-button:hover), :host ::slotted(cr-button:hover) { background-color: transparent !important; }
diff --git a/ui/webui/resources/cr_elements/paper_button_style_css.html b/ui/webui/resources/cr_elements/paper_button_style_css.html deleted file mode 100644 index 386b57e0..0000000 --- a/ui/webui/resources/cr_elements/paper_button_style_css.html +++ /dev/null
@@ -1,170 +0,0 @@ -<link rel="import" href="../html/polymer.html"> - -<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> -<link rel="import" href="shared_vars_css.html"> - -<!-- Common paper-button styling for Material Design WebUI. --> -<dom-module id="paper-button-style"> - <template> - <style> - paper-button { - --active-shadow-rgb: var(--google-grey-800-rgb); - --active-shadow-action-rgb: var(--google-blue-500-rgb); - - --border-color: var(--google-grey-refresh-300); - - --disabled-color: var(--google-grey-refresh-100); - --disabled-text-color: var(--google-grey-600); - - --flat-bg-action: var(--google-blue-600); - --flat-disabled-bg: white; - --flat-disabled-border-color: var(--google-grey-refresh-100); - --flat-focus-shadow-color: rgba(var(--google-blue-600-rgb), .4); - --flat-ink-color-action: white; - --flat-ripple-opacity-action: .32; - --flat-text-color-action: white; - - --hover-bg-action: rgba(var(--google-blue-600-rgb), .9); - --hover-bg-color: rgba(var(--google-blue-500-rgb), .04); - --hover-border-color: var(--google-blue-refresh-100); - --hover-shadow-action-rgb: var(--google-blue-500-rgb); - - /* Blue-ish color used either as a background or as a text color, - * depending on the type of button. */ - --paper-button-ink-color: var(--google-blue-600); - - --ripple-opacity: .1; - - --text-color: var(--google-blue-600); - } - - @media (prefers-color-scheme: dark) { - paper-button { - /* Only in dark. */ - --active-bg: black linear-gradient(rgba(255, 255, 255, .06), - rgba(255, 255, 255, .06)); - --active-shadow-rgb: 0, 0, 0; - --active-shadow-action-rgb: var(--google-blue-refresh-500-rgb); - - --border-color: var(--google-grey-refresh-700); - - --disabled-color: var(--google-grey-800); - /* --disabled-text-color is same as light mode (GG 600). */ - - --flat-bg-action: var(--google-blue-refresh-300); - /* TODO(dbeam): get --flat-disabled-bg from Namrata. */ - --flat-disabled-bg: transparent; - --flat-disabled-border-color: var(--google-grey-800); - --flat-focus-shadow-color: rgba(var(--google-blue-refresh-300-rgb), .5); - --flat-ink-color-action: black; - --flat-ripple-opacity-action: .16; - --flat-text-color-action: var(--google-grey-900); - - --hover-bg-action: var(--flat-bg-action) - linear-gradient(rgba(0, 0, 0, .08), rgba(0, 0, 0, .08)); - --hover-bg-color: rgba(var(--google-blue-refresh-300-rgb), .08); - /* No change in secondary box-shadow or border color on hover - * (--hover-border-color, --hover-shadow-action-rgb) in dark mode. */ - - --paper-button-ink-color: var(--google-blue-refresh-300); - - --ripple-opacity: .16; - - --text-color: var(--google-blue-refresh-300); - } - } - - paper-button { - --paper-ripple-opacity: var(--ripple-opacity); - border: 1px solid var(--border-color); - border-radius: 4px; - color: var(--text-color); - flex-shrink: 0; - font-weight: 500; - height: var(--cr-button-height); - margin: 0; - padding: 8px 16px; - text-decoration: none; - text-transform: none; - } - - /* Override paper-button's default transition. |animated| is automatically - * added to all paper-button instances, unfortunately. */ - paper-button[animated] { - transition: none; - } - - paper-button:not([raised]).keyboard-focus, - paper-button:not([raised]).action-button.keyboard-focus { - box-shadow: 0 0 0 2px var(--flat-focus-shadow-color); - /* Override default paper-button's internal font-weight style. */ - font-weight: 500; - } - - paper-button:not(.action-button):hover { - background-color: var(--hover-bg-color); - } - - @media (prefers-color-scheme: light) { - paper-button:not(.action-button):hover { - border-color: var(--hover-border-color); - } - } - - paper-button:not(.action-button):active { - background: var(--active-bg); - box-shadow: - 0 1px 2px 0 rgba(var(--active-shadow-rgb), .3), - 0 3px 6px 2px rgba(var(--active-shadow-rgb), .15); - } - - paper-button:not([raised]).action-button:hover { - background: var(--hover-bg-action); - } - - @media (prefers-color-scheme: light) { - paper-button.action-button:hover { - box-shadow: - 0 1px 2px 0 rgba(var(--hover-shadow-action-rgb), .3), - 0 1px 3px 1px rgba(var(--hover-shadow-action-rgb), .15); - } - } - - paper-button.action-button:active { - box-shadow: - 0 1px 2px 0 rgba(var(--active-shadow-action-rgb), .3), - 0 3px 6px 2px rgba(var(--active-shadow-action-rgb), .15); - } - - paper-button:not([raised]).action-button { - --paper-button-ink-color: var(--flat-ink-color-action); - --paper-ripple-opacity: var(--flat-ripple-opacity-action); - background-color: var(--flat-bg-action); - border: none; - color: var(--flat-text-color-action); - } - - paper-button:not([raised])[disabled] { - background-color: var(--flat-disabled-bg); - border-color: var(--flat-disabled-border-color); - color: var(--disabled-text-color); - } - - paper-button:not([raised]).action-button[disabled] { - background-color: var(--disabled-color); - border-color: transparent; - color: var(--disabled-text-color); - } - - /* cancel-button is meant to be used within a cr-dialog */ - paper-button.cancel-button { - margin-inline-end: 8px; - } - - paper-button.action-button, - paper-button.cancel-button { - line-height: 154%; - } - </style> - </template> -</dom-module>
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp index 45ff4f2f..15d93b19 100644 --- a/ui/webui/resources/cr_elements_resources.grdp +++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -388,10 +388,6 @@ file="cr_elements/md_select_css.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML" - file="cr_elements/paper_button_style_css.html" - type="chrome_html" - compress="gzip" /> <structure name="IDR_CR_ELEMENTS_SEARCH_HIGHLIGHT_STYLE_CSS_HTML" file="cr_elements/search_highlight_style_css.html" type="chrome_html"
diff --git a/ui/webui/resources/polymer_resources.grdp b/ui/webui/resources/polymer_resources.grdp index cf84a6f5..7e7773cf 100644 --- a/ui/webui/resources/polymer_resources.grdp +++ b/ui/webui/resources/polymer_resources.grdp
@@ -384,14 +384,6 @@ file="../../../third_party/polymer/v1_0/components-chromium/neon-animation/web-animations.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_BEHAVIORS_PAPER_BUTTON_BEHAVIOR_EXTRACTED_JS" - file="../../../third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior-extracted.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_BEHAVIORS_PAPER_BUTTON_BEHAVIOR_HTML" - file="../../../third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-button-behavior.html" - type="chrome_html" - compress="gzip" /> <structure name="IDR_POLYMER_1_0_PAPER_BEHAVIORS_PAPER_RIPPLE_BEHAVIOR_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-ripple-behavior-extracted.js" type="chrome_html" @@ -400,14 +392,6 @@ file="../../../third_party/polymer/v1_0/components-chromium/paper-behaviors/paper-ripple-behavior.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_BUTTON_PAPER_BUTTON_EXTRACTED_JS" - file="../../../third_party/polymer/v1_0/components-chromium/paper-button/paper-button-extracted.js" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_BUTTON_PAPER_BUTTON_HTML" - file="../../../third_party/polymer/v1_0/components-chromium/paper-button/paper-button.html" - type="chrome_html" - compress="gzip" /> <if expr="chromeos"> <structure name="IDR_POLYMER_1_0_PAPER_INPUT_PAPER_INPUT_ADDON_BEHAVIOR_EXTRACTED_JS" file="../../../third_party/polymer/v1_0/components-chromium/paper-input/paper-input-addon-behavior-extracted.js"
diff --git a/ui/webui/resources/polymer_resources_v3.grdp b/ui/webui/resources/polymer_resources_v3.grdp index db114b80..20532cd2 100644 --- a/ui/webui/resources/polymer_resources_v3.grdp +++ b/ui/webui/resources/polymer_resources_v3.grdp
@@ -218,10 +218,6 @@ type="chrome_html" compress="gzip" /> </if> - <structure name="IDR_POLYMER_3_0_PAPER_BEHAVIORS_PAPER_BUTTON_BEHAVIOR_JS" - file="../../../third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-button-behavior.js" - type="chrome_html" - compress="gzip" /> <structure name="IDR_POLYMER_3_0_PAPER_BEHAVIORS_PAPER_INKY_FOCUS_BEHAVIOR_JS" file="../../../third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-inky-focus-behavior.js" type="chrome_html" @@ -230,10 +226,6 @@ file="../../../third_party/polymer/v3_0/components-chromium/paper-behaviors/paper-ripple-behavior.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_3_0_PAPER_BUTTON_PAPER_BUTTON_JS" - file="../../../third_party/polymer/v3_0/components-chromium/paper-button/paper-button.js" - type="chrome_html" - compress="gzip" /> <if expr="chromeos"> <structure name="IDR_POLYMER_3_0_PAPER_INPUT_PAPER_INPUT_ADDON_BEHAVIOR_JS" file="../../../third_party/polymer/v3_0/components-chromium/paper-input/paper-input-addon-behavior.js"