diff --git a/BUILD.gn b/BUILD.gn index 7f12f0a..ce3ab61f 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -216,7 +216,10 @@ } else if (is_ios) { deps += [ "//ios:all" ] } else if (is_fuchsia) { - deps += [ ":d8_fuchsia" ] + deps += [ + ":d8_fuchsia", + "tools/fuchsia/fidlgen_js:fidlgen_js_unittests", + ] } deps += root_extra_deps
diff --git a/DEPS b/DEPS index fd69032..3e32d82d 100644 --- a/DEPS +++ b/DEPS
@@ -105,7 +105,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': '49fdd7ad1875f0a26317921367f6e21a60493ca8', + 'skia_revision': '9e0efe3189e0212c6936a9d3103f222ebe46bc1f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -117,7 +117,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'c3bef3e7b0280aa367187921fdecf059796e7b65', + 'angle_revision': 'c2116cd3af9ff11f783aaf80e8a48c3f089ae8ea', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling build tools # and whatever else without interference from each other. @@ -129,7 +129,7 @@ # 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': 'a1c846c4cf3f8c08edfffa1cc6b60860c011000b', + 'pdfium_revision': '2ff6cd661c0203dcdcc09135bce8bba141037574', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -181,7 +181,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'feed_revision': '3c5a5efeacbb25d349b36c3482c5794c5229e869', + 'feed_revision': 'e291161061d18d222bc1b546225fe0567386cbf1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling android_sdk_build-tools_version # and whatever else without interference from each other. @@ -213,7 +213,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'spv_tools_revision': 'd73b9d8dfbf7761e3fde323af00ec18ebfc0020c', + 'spv_tools_revision': '19c07731fce3073ea85e4f3e8759d9662b606f56', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -610,7 +610,7 @@ # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + '536735c009117dc648808dfe47e55284deca8fa0', + 'url': Var('chromium_git') + '/chromiumos/platform/system_api.git' + '@' + 'f63dc2e08f0c619b185cd48de3dcf758bcd9d109', 'condition': 'checkout_linux', }, @@ -620,7 +620,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '22300e1fb562291b55eb702fe73b164cb1a2317d', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '684313d6a319a33b8aef29cd0600e2dcbe26f8db', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -954,7 +954,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cd9050823b919817b45ef6f104d0c95b61ec2a43', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '723e82b649de19b925c286b5af838c3f45037c56', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1106,7 +1106,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'f9d38f2e4e5d87f3a1be0711b385d9809c9281f8', + Var('webrtc_git') + '/src.git' + '@' + '59021ba4e1dcd6b5dddf966591a4d0ebc6862705', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1137,7 +1137,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@457799c2a3affc68076fabbd04c4a56f6b452ce2', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@18c0a2b447779c4f5942d25be801168b8343d333', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/aw_variations_service_client.cc b/android_webview/browser/aw_variations_service_client.cc index e096879..7781ad84 100644 --- a/android_webview/browser/aw_variations_service_client.cc +++ b/android_webview/browser/aw_variations_service_client.cc
@@ -54,6 +54,10 @@ return android_webview::GetChannelOrStable(); } +bool AwVariationsServiceClient::GetSupportsPermanentConsistency() { + return false; +} + bool AwVariationsServiceClient::OverridesRestrictParameter( std::string* parameter) { return false;
diff --git a/android_webview/browser/aw_variations_service_client.h b/android_webview/browser/aw_variations_service_client.h index afcac6b..0caf0fd 100644 --- a/android_webview/browser/aw_variations_service_client.h +++ b/android_webview/browser/aw_variations_service_client.h
@@ -31,6 +31,7 @@ scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; network_time::NetworkTimeTracker* GetNetworkTimeTracker() override; version_info::Channel GetChannel() override; + bool GetSupportsPermanentConsistency() override; bool OverridesRestrictParameter(std::string* parameter) override; DISALLOW_COPY_AND_ASSIGN(AwVariationsServiceClient);
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn index 9b8958e..e97103b 100644 --- a/android_webview/glue/BUILD.gn +++ b/android_webview/glue/BUILD.gn
@@ -43,6 +43,7 @@ "java/src/com/android/webview/chromium/ServiceWorkerSettingsAdapter.java", "java/src/com/android/webview/chromium/SharedStatics.java", "java/src/com/android/webview/chromium/SharedTracingControllerAdapter.java", + "java/src/com/android/webview/chromium/SplitApkWorkaround.java", "java/src/com/android/webview/chromium/TokenBindingManagerAdapter.java", "java/src/com/android/webview/chromium/TracingControllerAdapter.java", "java/src/com/android/webview/chromium/WebBackForwardListChromium.java",
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java b/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java new file mode 100644 index 0000000..b403474 --- /dev/null +++ b/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java
@@ -0,0 +1,155 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package com.android.webview.chromium; + +import android.support.annotation.IntDef; + +import dalvik.system.BaseDexClassLoader; + +import org.chromium.base.Log; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * WebView-side workaround for the Android O framework bug described in https://crbug.com/889954 + * which affects us if we are the current WebView provider and we were installed as a split APK. + */ +public class SplitApkWorkaround { + private static final String TAG = "SplitApkWorkaround"; + + @IntDef({Result.NOT_RUN, Result.NO_ENTRIES, Result.ONE_ENTRY, Result.MULTIPLE_ENTRIES, + Result.TOPLEVEL_EXCEPTION, Result.LOOP_EXCEPTION}) + @interface Result { + int NOT_RUN = 0; + int NO_ENTRIES = 1; + int ONE_ENTRY = 2; + int MULTIPLE_ENTRIES = 3; + int TOPLEVEL_EXCEPTION = 4; + int LOOP_EXCEPTION = 5; + } + + /** + * There is a framework bug in O that causes an incorrect classloader cache entry to be created + * when the WebView provider is installed as multiple split APKs. + * Use reflection to correct the cache entry during WebView zygote startup. + * This function runs in the WebView zygote, which cannot make any binder calls to the framework + * and is a very restricted environment. + * + * @param dryRun If true, don't actually change any state in the framework; just verify that + * the reflection succeeds. + * @return a value from Result describing what happened. + */ + @SuppressWarnings("unchecked") + public static @Result int apply(boolean dryRun) { + int matchingEntries = 0; + int exceptionEntries = 0; + try { + // Retrieve all the required classes and fields first, such that if any of the lookups + // fail, we won't have done anything yet. + Class<?> alClass = Class.forName("android.app.ApplicationLoaders"); + Method getDefaultMethod = alClass.getDeclaredMethod("getDefault"); + Field mLoadersField = alClass.getDeclaredField("mLoaders"); + mLoadersField.setAccessible(true); + Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList"); + pathListField.setAccessible(true); + Class<?> dplClass = Class.forName("dalvik.system.DexPathList"); + Field dexElementsField = dplClass.getDeclaredField("dexElements"); + dexElementsField.setAccessible(true); + Class<?> elClass = Class.forName("dalvik.system.DexPathList$Element"); + Field pathField = elClass.getDeclaredField("path"); + pathField.setAccessible(true); + + // Retrieve the ApplicationLoaders singleton and get the cache from inside it. + Object alInstance = getDefaultMethod.invoke(null); + Object rawLoaders = mLoadersField.get(alInstance); + Map<String, ClassLoader> loaders = (Map<String, ClassLoader>) rawLoaders; + + // Synchronize on the map while trying to update it, as the framework does. + synchronized (loaders) { + for (Map.Entry<String, ClassLoader> entry : loaders.entrySet()) { + try { + if (!(entry.getValue() instanceof BaseDexClassLoader)) { + // If it's some other type it can't be the right one. + continue; + } + String cacheKey = entry.getKey(); + BaseDexClassLoader cl = (BaseDexClassLoader) entry.getValue(); + + // Get the list of files that this classloader uses as its classpath. + Object pathList = pathListField.get(cl); + Object dexElements = dexElementsField.get(pathList); + + int elementCount = Array.getLength(dexElements); + if (elementCount <= 1) { + // If there's only one file, then this classloader cannot be affected by + // the bug, so ignore it. + continue; + } + + // If there's more than one file, get the first file in the path. + // If it's the same as our cache key, then the cache key must, by + // definition, be incomplete - listing only one file when it should list + // all of them. + Object firstElement = Array.get(dexElements, 0); + File firstPath = (File) pathField.get(firstElement); + if (cacheKey.equals(firstPath.getPath())) { + // Build a new, correct cache key by concatenating all the files in the + // list together in order, separated by colons. + String newCacheKey = cacheKey; + for (int i = 1; i < elementCount; i++) { + Object element = Array.get(dexElements, i); + File path = (File) pathField.get(element); + newCacheKey += ":" + path.getPath(); + } + + matchingEntries++; + if (!dryRun) { + // Add a new entry to the cache which maps the new, correct key to + // the same classloader object. We do not remove the previous entry + // from the cache, in case something attempts to look it up by the + // old key for some reason - it shouldn't cause a problem for there + // to be multiple entries mapping to the same classloader. + loaders.put(newCacheKey, cl); + Log.i(TAG, "Fixed classloader cache entry for " + newCacheKey); + } + } + } catch (Exception e) { + // We log and ignore it here so we can continue looping through the cache, + // in the hope that the one that threw an exception wasn't the one we + // were looking for. + exceptionEntries++; + Log.w(TAG, "Caught exception while attempting to fix classloader cache", e); + } + } + } + } catch (Exception e) { + // If we got an exception at this point we assume that we failed to fix it, since we + // didn't get as far as iterating over the cache entries. + Log.w(TAG, "Caught exception while attempting to fix classloader cache", e); + return Result.TOPLEVEL_EXCEPTION; + } + + // If we found at least one matching entry, then don't worry about exceptions that happened + // during the loop; we likely found the correct classloader, and the one that triggered an + // exception was probably not relevant. Distinguish one vs multiple entries, though, + // because multiple matches is unexpected (only one case in the code is supposed to create + // this situation). + if (matchingEntries == 1) return Result.ONE_ENTRY; + if (matchingEntries > 1) return Result.MULTIPLE_ENTRIES; + + // If we didn't find any matching entries, but did get an exception during the loop, then + // report this, as we might have taken the exception while trying to access the entry we + // needed to fix. + if (exceptionEntries > 0) return Result.LOOP_EXCEPTION; + + // Otherwise, we just didn't find any entries at all, which is probably fine; not all + // configurations actually trigger the bug. + return Result.NO_ENTRIES; + } +}
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java index 4243e08..75ad5153 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -13,7 +13,6 @@ import android.os.Build; import android.os.SystemClock; import android.provider.Settings; -import android.util.Log; import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; @@ -40,6 +39,7 @@ import org.chromium.base.BuildInfo; import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; +import org.chromium.base.Log; import org.chromium.base.PackageUtils; import org.chromium.base.PathUtils; import org.chromium.base.StrictModeContext; @@ -62,7 +62,7 @@ */ @SuppressWarnings("deprecation") public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { - private static final String TAG = "WebViewChromiumFactoryProvider"; + private static final String TAG = "WVCFactoryProvider"; private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs"; private static final String VERSION_CODE_PREF = "lastVersionCodeUsed"; @@ -362,7 +362,19 @@ } } + private static @SplitApkWorkaround.Result int sSplitApkWorkaroundResult = + SplitApkWorkaround.Result.NOT_RUN; + public static boolean preloadInZygote() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + // If we're on O, where the split APK handling bug exists, then go through the motions + // of applying the workaround - don't actually change anything, but do the reflection + // to check for compatibility issues. The result will be logged to UMA later, because + // we can't do very much in the restricted environment of the WebView zygote process. + sSplitApkWorkaroundResult = SplitApkWorkaround.apply(/* dryRun */ true); + } + for (String library : NativeLibraries.LIBRARIES) { System.loadLibrary(library); }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index bd69a39f..b044bbc 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -242,8 +242,6 @@ "cancel_mode.h", "cast_config_controller.cc", "cast_config_controller.h", - "client_image_registry.cc", - "client_image_registry.h", "dbus/ash_dbus_services.cc", "dbus/ash_dbus_services.h", "dbus/display_service_provider.cc",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc index 67548770c..7080726 100644 --- a/ash/accelerators/accelerator_controller.cc +++ b/ash/accelerators/accelerator_controller.cc
@@ -31,6 +31,7 @@ #include "ash/new_window_controller.h" #include "ash/public/cpp/app_list/app_list_constants.h" #include "ash/public/cpp/ash_features.h" +#include "ash/public/interfaces/accessibility_controller.mojom.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/root_window_controller.h" #include "ash/rotator/window_rotation.h" @@ -812,9 +813,8 @@ void HandleToggleDictation() { base::RecordAction(UserMetricsAction("Accel_Toggle_Dictation")); - UserMetricsRecorder::RecordUserToggleDictation( - DictationToggleMethod::kToggleByKeyboard); - Shell::Get()->accessibility_controller()->ToggleDictation(); + Shell::Get()->accessibility_controller()->ToggleDictationFromSource( + mojom::DictationToggleSource::kKeyboard); } bool CanHandleToggleDockedMagnifier() {
diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc index 86fde7b..5524fd8 100644 --- a/ash/accessibility/accessibility_controller.cc +++ b/ash/accessibility/accessibility_controller.cc
@@ -27,6 +27,7 @@ #include "ash/system/power/scoped_backlights_forced_off.h" #include "ash/wm/tablet_mode/tablet_mode_controller.h" #include "base/command_line.h" +#include "base/metrics/user_metrics.h" #include "base/strings/string16.h" #include "chromeos/audio/cras_audio_handler.h" #include "components/pref_registry/pref_registry_syncable.h" @@ -648,6 +649,14 @@ } } +void AccessibilityController::ToggleDictationFromSource( + mojom::DictationToggleSource source) { + base::RecordAction(base::UserMetricsAction("Accel_Toggle_Dictation")); + UserMetricsRecorder::RecordUserToggleDictation(source); + + ToggleDictation(); +} + void AccessibilityController::SilenceSpokenFeedback() { if (client_) client_->SilenceSpokenFeedback();
diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h index 1548b80..2da44e5 100644 --- a/ash/accessibility/accessibility_controller.h +++ b/ash/accessibility/accessibility_controller.h
@@ -167,6 +167,7 @@ void SetSelectToSpeakState(mojom::SelectToSpeakState state) override; void SetSelectToSpeakEventHandlerDelegate( mojom::SelectToSpeakEventHandlerDelegatePtr delegate) override; + void ToggleDictationFromSource(mojom::DictationToggleSource source) override; // SessionObserver: void OnSigninScreenPrefServiceInitialized(PrefService* prefs) override;
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index 8200dd1..7ac04ef0 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -33,16 +33,6 @@ #include "ui/base/ui_base_features.h" #include "ui/display/screen.h" -namespace { - -int64_t GetDisplayIdToShowAppListOn() { - return display::Screen::GetScreen() - ->GetDisplayNearestWindow(ash::Shell::GetRootWindowForNewWindows()) - .id(); -} - -} // namespace - namespace ash { AppListControllerImpl::AppListControllerImpl(ws::WindowService* window_service) @@ -387,15 +377,14 @@ void AppListControllerImpl::OnActiveUserPrefServiceChanged( PrefService* /* pref_service */) { - if (!IsHomeLauncherEnabledInTabletMode() || - !display::Display::HasInternalDisplay()) { + if (!IsHomeLauncherEnabledInTabletMode()) { DismissAppList(); return; } // Show the app list after signing in in tablet mode. - Show(display::Display::InternalDisplayId(), - app_list::AppListShowSource::kTabletMode, base::TimeTicks()); + Show(GetDisplayIdToShowAppListOn(), app_list::AppListShowSource::kTabletMode, + base::TimeTicks()); // The app list is not dismissed before switching user, suggestion chips will // not be shown. So reset app list state and trigger an initial search here to @@ -536,7 +525,7 @@ presenter_.GetView()->OnTabletModeChanged(true); } - if (!is_home_launcher_enabled_ || !display::Display::HasInternalDisplay()) + if (!is_home_launcher_enabled_) return; SessionController const* session_controller = @@ -545,8 +534,7 @@ return; // Show the app list if the tablet mode starts. - Show(display::Display::InternalDisplayId(), app_list::kTabletMode, - base::TimeTicks()); + Show(GetDisplayIdToShowAppListOn(), app_list::kTabletMode, base::TimeTicks()); UpdateHomeLauncherVisibility(); Shelf::ForWindow(presenter_.GetWindow())->MaybeUpdateShelfBackground(); } @@ -865,4 +853,16 @@ controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED); } +int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() { + if (IsHomeLauncherEnabledInTabletMode()) { + return display::Display::HasInternalDisplay() + ? display::Display::InternalDisplayId() + : display::Screen::GetScreen()->GetPrimaryDisplay().id(); + } + + return display::Screen::GetScreen() + ->GetDisplayNearestWindow(ash::Shell::GetRootWindowForNewWindows()) + .id(); +} + } // namespace ash
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h index 35d3000..ff3de81 100644 --- a/ash/app_list/app_list_controller_impl.h +++ b/ash/app_list/app_list_controller_impl.h
@@ -235,6 +235,8 @@ // Update the visibility of Assistant functionality. void UpdateAssistantVisibility(); + int64_t GetDisplayIdToShowAppListOn(); + ws::WindowService* window_service_; base::string16 last_raw_query_;
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc index e82449f4..0ef342a 100644 --- a/ash/app_list/app_list_presenter_delegate_unittest.cc +++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -1124,9 +1124,6 @@ app_list_features::kEnableBackgroundBlur}, {}); AppListPresenterDelegateTest::SetUp(); - // Home launcher is only enabled on internal display. - display::test::DisplayManagerTestApi(Shell::Get()->display_manager()) - .SetFirstDisplayAsInternalDisplay(); GetAppListTestHelper()->WaitUntilIdle(); }
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc index dbc45d7..f211bad 100644 --- a/ash/app_list/presenter/app_list_presenter_impl.cc +++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -136,6 +136,7 @@ delegate_->Init(view, display_id, current_apps_page_); SetView(view); } + view_->ShowWhenReady(); delegate_->OnShown(display_id); NotifyTargetVisibilityChanged(GetTargetVisibility()); NotifyVisibilityChanged(GetTargetVisibility(), display_id); @@ -279,7 +280,6 @@ // Sync the |onscreen_keyboard_shown_| in case |view_| is not initiated when // the on-screen is shown. view_->set_onscreen_keyboard_shown(delegate_->GetOnScreenKeyboardShown()); - view_->ShowWhenReady(); } void AppListPresenterImpl::ResetView() {
diff --git a/ash/client_image_registry.cc b/ash/client_image_registry.cc deleted file mode 100644 index 2567ac1..0000000 --- a/ash/client_image_registry.cc +++ /dev/null
@@ -1,39 +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 "ash/client_image_registry.h" - -namespace ash { - -ClientImageRegistry::ClientImageRegistry() = default; - -ClientImageRegistry::~ClientImageRegistry() = default; - -void ClientImageRegistry::BindRequest( - mojom::ClientImageRegistryRequest request) { - binding_set_.AddBinding(this, std::move(request)); -} - -const gfx::ImageSkia* ClientImageRegistry::GetImage( - const base::UnguessableToken& token) const { - auto iter = images_.find(token); - if (iter == images_.end()) { - // No DCHECK here as otherwise ash would crash if a bad client supplies a - // random value. - return nullptr; - } - - return &iter->second; -} - -void ClientImageRegistry::RegisterImage(const base::UnguessableToken& token, - const gfx::ImageSkia& image) { - images_[token] = image; -} - -void ClientImageRegistry::ForgetImage(const base::UnguessableToken& token) { - images_.erase(token); -} - -} // namespace ash
diff --git a/ash/client_image_registry.h b/ash/client_image_registry.h deleted file mode 100644 index d59e4d1..0000000 --- a/ash/client_image_registry.h +++ /dev/null
@@ -1,45 +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 ASH_CLIENT_IMAGE_REGISTRY_H_ -#define ASH_CLIENT_IMAGE_REGISTRY_H_ - -#include <map> - -#include "ash/public/interfaces/client_image_registry.mojom.h" -#include "base/macros.h" -#include "base/unguessable_token.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "ui/gfx/image/image_skia.h" - -namespace ash { - -// ClientImageRegistry holds onto images that clients provide until it's told to -// drop them. This allows reuse of an image with making multiple copies in the -// Ash process or repeated serialization/deserialization. -class ClientImageRegistry : public mojom::ClientImageRegistry { - public: - ClientImageRegistry(); - ~ClientImageRegistry() override; - - void BindRequest(mojom::ClientImageRegistryRequest request); - - const gfx::ImageSkia* GetImage(const base::UnguessableToken& token) const; - - // mojom::ClientImageRegistry: - void RegisterImage(const base::UnguessableToken& token, - const gfx::ImageSkia& image) override; - void ForgetImage(const base::UnguessableToken& token) override; - - private: - std::map<base::UnguessableToken, gfx::ImageSkia> images_; - - mojo::BindingSet<mojom::ClientImageRegistry> binding_set_; - - DISALLOW_COPY_AND_ASSIGN(ClientImageRegistry); -}; - -} // namespace ash - -#endif // ASH_CLIENT_IMAGE_REGISTRY_H_
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc index 531a72f3..88bb980 100644 --- a/ash/drag_drop/drag_drop_controller.cc +++ b/ash/drag_drop/drag_drop_controller.cc
@@ -179,7 +179,7 @@ // capture, it still gets a valid gesture state. Shell::Get()->aura_env()->gesture_recognizer()->TransferEventsTo( source_window, tracker->capture_window(), - ui::GestureRecognizer::ShouldCancelTouches::Cancel); + ui::TransferTouchesBehavior::kCancel); // We also send a gesture end to the source window so it can clear state. // TODO(varunjain): Remove this whole block when gesture sequence // transferring is properly done in the GR (http://crbug.com/160558)
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc index d08ac5bc..1942e68d 100644 --- a/ash/frame/header_view.cc +++ b/ash/frame/header_view.cc
@@ -6,7 +6,6 @@ #include <memory> -#include "ash/client_image_registry.h" #include "ash/frame/non_client_frame_view_ash.h" #include "ash/public/cpp/caption_buttons/caption_button_model.h" #include "ash/public/cpp/caption_buttons/frame_back_button.h" @@ -326,12 +325,7 @@ if (!target_widget_) return; - caption_button_container_->SetVisible( - should_paint_ && !(Shell::Get() - ->tablet_mode_controller() - ->IsTabletModeWindowManagerEnabled() && - target_widget_->GetNativeWindow()->GetProperty( - ash::kHideCaptionButtonsInTabletModeKey))); + caption_button_container_->SetVisible(should_paint_); } } // namespace ash
diff --git a/ash/frame/non_client_frame_view_ash_unittest.cc b/ash/frame/non_client_frame_view_ash_unittest.cc index e8349a96..8753d54f 100644 --- a/ash/frame/non_client_frame_view_ash_unittest.cc +++ b/ash/frame/non_client_frame_view_ash_unittest.cc
@@ -9,7 +9,6 @@ #include "ash/accelerators/accelerator_controller.h" #include "ash/frame/header_view.h" #include "ash/frame/wide_frame_view.h" -#include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/ash_layout_constants.h" #include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/caption_buttons/frame_caption_button.h" @@ -29,7 +28,6 @@ #include "ash/wm/wm_event.h" #include "base/command_line.h" #include "base/containers/flat_set.h" -#include "base/test/scoped_feature_list.h" #include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" @@ -927,81 +925,4 @@ // Run frame color tests with and without custom wm::WindowStateDelegate. INSTANTIATE_TEST_CASE_P(, NonClientFrameViewAshFrameColorTest, testing::Bool()); -class HomeLauncherNonClientFrameViewAshTest : public AshTestBase { - public: - HomeLauncherNonClientFrameViewAshTest() = default; - ~HomeLauncherNonClientFrameViewAshTest() override = default; - - void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - app_list_features::kEnableHomeLauncher); - AshTestBase::SetUp(); - } - - private: - base::test::ScopedFeatureList scoped_feature_list_; - - DISALLOW_COPY_AND_ASSIGN(HomeLauncherNonClientFrameViewAshTest); -}; - -// Tests the visibility of the caption button container when -// kHideCaptionButtonsInTabletModeKey is set. -TEST_F(HomeLauncherNonClientFrameViewAshTest, - TabletModeBrowserCaptionButtonVisibility) { - auto* delegate = new NonClientFrameViewAshTestWidgetDelegate(); - std::unique_ptr<views::Widget> widget = CreateTestWidget( - delegate, kShellWindowId_DefaultContainer, gfx::Rect(100, 0, 400, 500)); - widget->GetNativeWindow()->SetProperty(kHideCaptionButtonsInTabletModeKey, - true); - - FrameCaptionButtonContainerView* caption_buttons = - delegate->non_client_frame_view() - ->GetHeaderView() - ->caption_button_container(); - - EXPECT_TRUE(caption_buttons->visible()); - ash::Shell* shell = ash::Shell::Get(); - ash::TabletModeController* tablet_mode_controller = - shell->tablet_mode_controller(); - tablet_mode_controller->EnableTabletModeWindowManager(true); - EXPECT_FALSE(caption_buttons->visible()); - - shell->window_selector_controller()->ToggleOverview(); - EXPECT_FALSE(caption_buttons->visible()); - shell->window_selector_controller()->ToggleOverview(); - EXPECT_FALSE(caption_buttons->visible()); - - tablet_mode_controller->EnableTabletModeWindowManager(false); - EXPECT_TRUE(caption_buttons->visible()); -} - -// Tests the visibility of the caption button container when -// kHideCaptionButtonsInTabletModeKey is not set. -TEST_F(HomeLauncherNonClientFrameViewAshTest, - TabletModeAppCaptionButtonVisibility) { - auto* delegate = new NonClientFrameViewAshTestWidgetDelegate(); - std::unique_ptr<views::Widget> widget = CreateTestWidget( - delegate, kShellWindowId_DefaultContainer, gfx::Rect(100, 0, 400, 500)); - - FrameCaptionButtonContainerView* caption_buttons = - delegate->non_client_frame_view() - ->GetHeaderView() - ->caption_button_container(); - - EXPECT_TRUE(caption_buttons->visible()); - ash::Shell* shell = ash::Shell::Get(); - ash::TabletModeController* tablet_mode_controller = - shell->tablet_mode_controller(); - tablet_mode_controller->EnableTabletModeWindowManager(true); - EXPECT_TRUE(caption_buttons->visible()); - - shell->window_selector_controller()->ToggleOverview(); - EXPECT_FALSE(caption_buttons->visible()); - shell->window_selector_controller()->ToggleOverview(); - EXPECT_TRUE(caption_buttons->visible()); - - tablet_mode_controller->EnableTabletModeWindowManager(false); - EXPECT_TRUE(caption_buttons->visible()); -} - } // namespace ash
diff --git a/ash/login/ui/lock_screen.cc b/ash/login/ui/lock_screen.cc index caf4a68..e3e05115 100644 --- a/ash/login/ui/lock_screen.cc +++ b/ash/login/ui/lock_screen.cc
@@ -16,7 +16,6 @@ #include "ash/tray_action/tray_action.h" #include "ash/wallpaper/wallpaper_controller.h" #include "base/command_line.h" -#include "base/timer/timer.h" #include "chromeos/chromeos_switches.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -25,9 +24,6 @@ namespace ash { namespace { -constexpr base::TimeDelta kShowLoginScreenTimeout = - base::TimeDelta::FromSeconds(5); - // Global lock screen instance. There can only ever be on lock screen at a // time. LockScreen* instance_ = nullptr; @@ -86,28 +82,18 @@ } instance_->window_->set_data_dispatcher(std::move(data_dispatcher)); - const base::RepeatingClosure show_screen = base::BindRepeating([]() { - // |instance_| may already be destroyed in tests. - if (!instance_ || instance_->is_shown_) - return; - instance_->is_shown_ = true; - instance_->window_->Show(); - }); - if (type == ScreenType::kLogin) { - // Postpone showing the login screen after the animation of the first - // wallpaper completes, to make the transition smooth. - Shell::Get()->wallpaper_controller()->AddFirstWallpaperAnimationEndCallback( - show_screen, instance_->window_->GetNativeView()); - // In case the wallpaper animation takes forever to complete, set a timer to - // make sure the login screen is shown eventually. This should never happen, - // so use an extra long time-out value to raise awareness. - instance_->show_login_screen_fallback_timer_ = - std::make_unique<base::OneShotTimer>(); - instance_->show_login_screen_fallback_timer_->Start( - FROM_HERE, kShowLoginScreenTimeout, show_screen); - } else { - show_screen.Run(); - } + // Postpone showing the screen after the animation of the first wallpaper + // completes, to make the transition smooth. The callback will be dispatched + // immediately if the animation is already complete (e.g. kLock). + Shell::Get()->wallpaper_controller()->AddFirstWallpaperAnimationEndCallback( + base::BindOnce([]() { + // |instance_| may already be destroyed in tests. + if (!instance_ || instance_->is_shown_) + return; + instance_->is_shown_ = true; + instance_->window_->Show(); + }), + instance_->window_->GetNativeView()); } // static
diff --git a/ash/login/ui/lock_screen.h b/ash/login/ui/lock_screen.h index 718044c..3377acb 100644 --- a/ash/login/ui/lock_screen.h +++ b/ash/login/ui/lock_screen.h
@@ -5,18 +5,12 @@ #ifndef ASH_LOGIN_UI_LOCK_SCREEN_H_ #define ASH_LOGIN_UI_LOCK_SCREEN_H_ -#include <memory> - #include "ash/ash_export.h" #include "ash/session/session_observer.h" #include "ash/tray_action/tray_action_observer.h" #include "base/macros.h" #include "base/scoped_observer.h" -namespace base { -class OneShotTimer; -} - namespace ash { class LockContentsView; @@ -89,10 +83,6 @@ // Unowned pointer to the LockContentsView hosted in lock window. LockContentsView* contents_view_ = nullptr; - // The fallback timer that ensures the login screen is shown in case the first - // wallpaper animation takes an extra long time to complete. - std::unique_ptr<base::OneShotTimer> show_login_screen_fallback_timer_; - bool is_shown_ = false; ScopedObserver<TrayAction, TrayActionObserver> tray_action_observer_{this};
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc index 2ab29e3..680e38b9 100644 --- a/ash/login/ui/login_keyboard_test_base.cc +++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -73,6 +73,8 @@ void LoginKeyboardTestBase::ShowLockScreen() { GetSessionControllerClient()->SetSessionState( session_manager::SessionState::LOCKED); + // The lock screen can't be shown without a wallpaper. + Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting(); base::Optional<bool> result; login_controller_->ShowLockScreen(base::BindOnce(
diff --git a/ash/manifest.json b/ash/manifest.json index bb10a14..fd53062 100644 --- a/ash/manifest.json +++ b/ash/manifest.json
@@ -20,7 +20,6 @@ "ash.mojom.AssistantController", "ash.mojom.AssistantVolumeControl", "ash.mojom.CastConfig", - "ash.mojom.ClientImageRegistry", "ash.mojom.CrosDisplayConfigController", "ash.mojom.DockedMagnifierController", "ash.mojom.EventRewriterController",
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc index 64a4de7..44b9a93 100644 --- a/ash/metrics/user_metrics_recorder.cc +++ b/ash/metrics/user_metrics_recorder.cc
@@ -13,6 +13,7 @@ #include "ash/public/cpp/shelf_item.h" #include "ash/public/cpp/shelf_model.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/interfaces/accessibility_controller.mojom-shared.h" #include "ash/public/interfaces/window_state_type.mojom.h" #include "ash/session/session_controller.h" #include "ash/shelf/shelf.h" @@ -200,9 +201,9 @@ // static void UserMetricsRecorder::RecordUserToggleDictation( - DictationToggleMethod method) { + mojom::DictationToggleSource source) { UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosDictation.ToggleDictationMethod", - method); + source); } void UserMetricsRecorder::RecordUserMetricsAction(UserMetricsAction action) {
diff --git a/ash/metrics/user_metrics_recorder.h b/ash/metrics/user_metrics_recorder.h index 9bb825e..4c8a4f7 100644 --- a/ash/metrics/user_metrics_recorder.h +++ b/ash/metrics/user_metrics_recorder.h
@@ -16,19 +16,14 @@ namespace ash { +namespace mojom { +enum class DictationToggleSource; +} // namespace mojom + class DemoSessionMetricsRecorder; class DesktopTaskSwitchMetricRecorder; class PointerMetricsRecorder; -// CrosDictationStartDictationMethod enum values. -// These values are persisted to logs and should not be renumbered or re-used. -// See tools/metrics/histograms/enums.xml. -enum class DictationToggleMethod { - kToggleByKeyboard, - kToggleByButton, - kMaxValue = kToggleByButton -}; - // User Metrics Recorder provides a repeating callback (RecordPeriodicMetrics) // on a timer to allow recording of state data over time to the UMA records. // Any additional states (in ash) that require monitoring can be added to @@ -51,7 +46,7 @@ LoginMetricsRecorder::ShelfButtonClickTarget target); // Record the method used to activate dictation. - static void RecordUserToggleDictation(DictationToggleMethod method); + static void RecordUserToggleDictation(mojom::DictationToggleSource source); // Records an Ash owned user action. void RecordUserMetricsAction(UserMetricsAction action);
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc index 8769de8..a0afe39 100644 --- a/ash/mojo_interface_factory.cc +++ b/ash/mojo_interface_factory.cc
@@ -12,7 +12,6 @@ #include "ash/app_list/app_list_controller_impl.h" #include "ash/assistant/assistant_controller.h" #include "ash/cast_config_controller.h" -#include "ash/client_image_registry.h" #include "ash/display/ash_display_controller.h" #include "ash/display/cros_display_config.h" #include "ash/display/display_output_protection.h" @@ -105,11 +104,6 @@ Shell::Get()->cast_config()->BindRequest(std::move(request)); } -void BindClientImageRegistryRequestOnMainThread( - mojom::ClientImageRegistryRequest request) { - Shell::Get()->client_image_registry()->BindRequest(std::move(request)); -} - void BindDisplayOutputProtectionRequestOnMainThread( mojom::DisplayOutputProtectionRequest request) { Shell::Get()->display_output_protection()->BindRequest(std::move(request)); @@ -258,9 +252,6 @@ registry->AddInterface(base::BindRepeating(&BindCastConfigOnMainThread), main_thread_task_runner); registry->AddInterface( - base::BindRepeating(&BindClientImageRegistryRequestOnMainThread), - main_thread_task_runner); - registry->AddInterface( base::BindRepeating(&BindDisplayOutputProtectionRequestOnMainThread), main_thread_task_runner); if (features::IsDockedMagnifierEnabled()) {
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc index 3dff51a..ca35d28 100644 --- a/ash/public/cpp/frame_header.cc +++ b/ash/public/cpp/frame_header.cc
@@ -164,7 +164,7 @@ void FrameHeader::SetBackButton(FrameCaptionButton* back_button) { back_button_ = back_button; if (back_button_) { - back_button_->SetColorMode(GetButtonColorMode()); + back_button_->SetColorMode(button_color_mode_); back_button_->SetBackgroundColor(GetCurrentFrameColor()); back_button_->SetImage(CAPTION_BUTTON_ICON_BACK, FrameCaptionButton::ANIMATE_NO, @@ -208,12 +208,10 @@ } void FrameHeader::UpdateCaptionButtonColors() { - auto button_color_mode = GetButtonColorMode(); - - caption_button_container_->SetColorMode(button_color_mode); + caption_button_container_->SetColorMode(button_color_mode_); caption_button_container_->SetBackgroundColor(GetCurrentFrameColor()); if (back_button_) { - back_button_->SetColorMode(button_color_mode); + back_button_->SetColorMode(button_color_mode_); back_button_->SetBackgroundColor(GetCurrentFrameColor()); } } @@ -303,11 +301,4 @@ GetHeaderHeight()); } -FrameCaptionButton::ColorMode FrameHeader::GetButtonColorMode() { - return target_widget()->GetNativeWindow()->GetProperty( - ash::kFrameIsThemedByHostedAppKey) - ? FrameCaptionButton::ColorMode::kThemed - : FrameCaptionButton::ColorMode::kDefault; -} - } // namespace ash
diff --git a/ash/public/cpp/frame_header.h b/ash/public/cpp/frame_header.h index 6ea1e75..f7b9011 100644 --- a/ash/public/cpp/frame_header.h +++ b/ash/public/cpp/frame_header.h
@@ -78,6 +78,10 @@ // gfx::AnimationDelegate: void AnimationProgressed(const gfx::Animation* animation) override; + void set_button_color_mode(FrameCaptionButton::ColorMode button_color_mode) { + button_color_mode_ = button_color_mode; + } + protected: FrameHeader(views::Widget* target_widget, views::View* view); @@ -127,7 +131,8 @@ gfx::Rect GetTitleBounds() const; - FrameCaptionButton::ColorMode GetButtonColorMode(); + FrameCaptionButton::ColorMode button_color_mode_ = + FrameCaptionButton::ColorMode::kDefault; // The widget that the caption buttons act on. This can be different from // |view_|'s widget.
diff --git a/ash/public/cpp/mus_property_mirror_ash.cc b/ash/public/cpp/mus_property_mirror_ash.cc index ced6296c..a63a5773 100644 --- a/ash/public/cpp/mus_property_mirror_ash.cc +++ b/ash/public/cpp/mus_property_mirror_ash.cc
@@ -85,13 +85,6 @@ } else if (key == kFrameInactiveColorKey) { root_window->SetProperty(kFrameInactiveColorKey, window->GetProperty(kFrameInactiveColorKey)); - } else if (key == kFrameIsThemedByHostedAppKey) { - root_window->SetProperty(kFrameIsThemedByHostedAppKey, - window->GetProperty(kFrameIsThemedByHostedAppKey)); - } else if (key == kHideCaptionButtonsInTabletModeKey) { - root_window->SetProperty( - kHideCaptionButtonsInTabletModeKey, - window->GetProperty(kHideCaptionButtonsInTabletModeKey)); } else if (key == kImmersiveImpliedByFullscreen) { root_window->SetProperty( kImmersiveImpliedByFullscreen,
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc index 910210e..5e3fee34 100644 --- a/ash/public/cpp/window_properties.cc +++ b/ash/public/cpp/window_properties.cc
@@ -60,17 +60,10 @@ ws::mojom::WindowManager::kFrameActiveColor_Property, aura::PropertyConverter::CreateAcceptAnyValueCallback()); property_converter->RegisterPrimitiveProperty( - kHideCaptionButtonsInTabletModeKey, - mojom::kHideCaptionButtonsInTabletMode_Property, - aura::PropertyConverter::CreateAcceptAnyValueCallback()); - property_converter->RegisterPrimitiveProperty( kFrameInactiveColorKey, ws::mojom::WindowManager::kFrameInactiveColor_Property, aura::PropertyConverter::CreateAcceptAnyValueCallback()); property_converter->RegisterPrimitiveProperty( - kFrameIsThemedByHostedAppKey, mojom::kFrameIsThemedByHostedApp_Property, - aura::PropertyConverter::CreateAcceptAnyValueCallback()); - property_converter->RegisterPrimitiveProperty( kHideShelfWhenFullscreenKey, mojom::kHideShelfWhenFullscreen_Property, aura::PropertyConverter::CreateAcceptAnyValueCallback()); property_converter->RegisterPrimitiveProperty( @@ -157,7 +150,6 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kBlockedForAssistantSnapshotKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCanAttachToAnotherWindowKey, true); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCanConsumeSystemKeysKey, false); -DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideCaptionButtonsInTabletModeKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInOverviewKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideShelfWhenFullscreenKey, true); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveImpliedByFullscreen, true); @@ -196,7 +188,6 @@ DEFINE_UI_CLASS_PROPERTY_KEY(SkColor, kFrameInactiveColorKey, kDefaultFrameColor); -DEFINE_UI_CLASS_PROPERTY_KEY(bool, kFrameIsThemedByHostedAppKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(mojom::WindowPinType, kWindowPinTypeKey, mojom::WindowPinType::NONE);
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h index 56faf5c..b035d499 100644 --- a/ash/public/cpp/window_properties.h +++ b/ash/public/cpp/window_properties.h
@@ -68,11 +68,6 @@ ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const kCanConsumeSystemKeysKey; -// A property to control the visibility of the frame captions buttons when in -// tablet mode (when not in tablet mode, this property is ignored). -ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const - kHideCaptionButtonsInTabletModeKey; - // A property key to indicate whether we should hide this window in overview // mode and Alt + Tab. ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const @@ -172,11 +167,6 @@ ASH_PUBLIC_EXPORT extern const aura::WindowProperty<SkColor>* const kFrameInactiveColorKey; -// True when the frame colors were provided by a hosted app, i.e. by a -// progressive web app manifest. -ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const - kFrameIsThemedByHostedAppKey; - // A property key to store ash::WindowPinType for a window. // When setting this property to PINNED or TRUSTED_PINNED, the window manager // will try to fullscreen the window and pin it on the top of the screen. If the
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn index 90a2c4e..25c34b3 100644 --- a/ash/public/interfaces/BUILD.gn +++ b/ash/public/interfaces/BUILD.gn
@@ -24,7 +24,6 @@ "assistant_setup.mojom", "assistant_volume_control.mojom", "cast_config.mojom", - "client_image_registry.mojom", "constants.mojom", "cros_display_config.mojom", "display_output_protection.mojom",
diff --git a/ash/public/interfaces/accessibility_controller.mojom b/ash/public/interfaces/accessibility_controller.mojom index 2d502ca..d34a5f8 100644 --- a/ash/public/interfaces/accessibility_controller.mojom +++ b/ash/public/interfaces/accessibility_controller.mojom
@@ -48,6 +48,22 @@ FULLSCREEN }; +// These values are persisted to logs and should not be renumbered or re-used. +// See tools/metrics/histograms/enums.xml. +enum DictationToggleSource { + // Toggled by the keyboard command (Search + D). + kKeyboard, + + // Toggled by the dictation button in the tray. + kButton, + + // Switch Access context menu button. + kSwitchAccess, + + // Chromevox chrome extension. + kChromevox +}; + enum SelectToSpeakState { // Select to Speak is not actively selecting text or speaking. kSelectToSpeakStateInactive, @@ -111,6 +127,9 @@ // Set the delegate used by the Select-to-Speak event handler. SetSelectToSpeakEventHandlerDelegate( SelectToSpeakEventHandlerDelegate delegate); + + // Starts or stops dictation. Records metrics for toggling via SwitchAccess. + ToggleDictationFromSource(DictationToggleSource source); }; // Interface for ash to request accessibility service from its client (e.g.
diff --git a/ash/public/interfaces/client_image_registry.mojom b/ash/public/interfaces/client_image_registry.mojom deleted file mode 100644 index 74aa2c8..0000000 --- a/ash/public/interfaces/client_image_registry.mojom +++ /dev/null
@@ -1,24 +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. - -module ash.mojom; - -import "mojo/public/mojom/base/unguessable_token.mojom"; -import "ui/gfx/image/mojo/image.mojom"; - -interface ClientImageRegistry { - // Tells Ash about an image which the client will later refer to by |token|. - // This allows clients such as Chrome to repeatedly use or reference the same - // image without serializing/deserializing every time. If the token already - // references another icon, this will replace it. - RegisterImage(mojo_base.mojom.UnguessableToken token, - gfx.mojom.ImageSkia icon); - - // Tells Ash that the client which registered the given token and its - // associated image will no longer use the image. This should be called at - // most once for every unique registered token. In the future, if Ash handles - // client restart, this will need to be called automatically for crashed - // clients. - ForgetImage(mojo_base.mojom.UnguessableToken token); -};
diff --git a/ash/public/interfaces/window_properties.mojom b/ash/public/interfaces/window_properties.mojom index f207357..ca01ba6 100644 --- a/ash/public/interfaces/window_properties.mojom +++ b/ash/public/interfaces/window_properties.mojom
@@ -18,31 +18,19 @@ const string kCanConsumeSystemKeys_Property = "ash:can-consume-system-keys"; -// A boolean that tells Ash whether the frame's colors come from a PWA manifest. -const string kFrameIsThemedByHostedApp_Property = - "ash:frame-is-themed-by-hosted-app"; - -// The color of the text drawn on the frame (i.e. the window title). Only used -// for tabless browser windows. -const string kFrameTextColor_Property = "ash:frame-text-color"; - -// See ash::kHideCaptionButtonsInTabletModeKey. -const string kHideCaptionButtonsInTabletMode_Property = - "ash:hide-caption-buttons-in-tablet-mode"; - // True if the shelf should be hidden when this window is put into fullscreen. // Exposed because some windows want to explicitly opt-out of this. const string kHideShelfWhenFullscreen_Property = "ash:hide-shelf-when-fullscreen"; -// See ash::kImmseriveImpliedByFullscreen. +// See ash::kImmersiveImpliedByFullscreen. const string kImmersiveImpliedByFullscreen_Property = "ash:immersive-implied-by-fullscreen"; -// See ash::kImmseriveIsActive. +// See ash::kImmersiveIsActive. const string kImmersiveIsActive_Property = "ash:immersive-is-active"; -// See ash::kImmseriveTopContainerBoundsInScreen. +// See ash::kImmersiveTopContainerBoundsInScreen. const string kImmersiveTopContainerBoundsInScreen_Property = "ash:immersive-top-container-bounds-in-screen";
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc index f7ce1a8..1ca93e52 100644 --- a/ash/shelf/app_list_shelf_item_delegate.cc +++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -75,6 +75,8 @@ if (back_action) Shell::Get()->app_list_controller()->Back(); + + std::move(callback).Run(SHELF_ACTION_APP_LIST_SHOWN, base::nullopt); } void AppListShelfItemDelegate::ExecuteCommand(bool from_context_menu,
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index 55ac89de..361b0ab 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -598,9 +598,9 @@ // Notify the item of its selection; handle the result in AfterItemSelected. model_->GetShelfItemDelegate(item.id)->ItemSelected( ui::Event::Clone(event), GetDisplayIdForView(this), LAUNCH_FROM_UNKNOWN, - base::Bind(&ShelfView::AfterItemSelected, weak_factory_.GetWeakPtr(), - item, sender, base::Passed(ui::Event::Clone(event)), - ink_drop)); + base::BindOnce(&ShelfView::AfterItemSelected, weak_factory_.GetWeakPtr(), + item, sender, base::Passed(ui::Event::Clone(event)), + ink_drop)); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/shell.cc b/ash/shell.cc index 113f7a4b..6d2d6b3 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -22,7 +22,6 @@ #include "ash/assistant/assistant_controller.h" #include "ash/autoclick/autoclick_controller.h" #include "ash/cast_config_controller.h" -#include "ash/client_image_registry.h" #include "ash/components/tap_visualizer/public/mojom/constants.mojom.h" #include "ash/dbus/ash_dbus_services.h" #include "ash/detachable_base/detachable_base_handler.h" @@ -1177,9 +1176,6 @@ power_button_controller_->OnDisplayModeChanged( display_configurator_->cached_displays()); - if (::features::IsUsingWindowService()) - client_image_registry_ = std::make_unique<ClientImageRegistry>(); - drag_drop_controller_ = std::make_unique<DragDropController>(); // |screenshot_controller_| needs to be created (and prepended as a
diff --git a/ash/shell.h b/ash/shell.h index 58dd502..1f7b854 100644 --- a/ash/shell.h +++ b/ash/shell.h
@@ -108,7 +108,6 @@ class BluetoothPowerController; class BrightnessControlDelegate; class CastConfigController; -class ClientImageRegistry; class DisplayOutputProtection; class CrosDisplayConfig; class DetachableBaseHandler; @@ -366,9 +365,6 @@ return brightness_control_delegate_.get(); } CastConfigController* cast_config() { return cast_config_.get(); } - ClientImageRegistry* client_image_registry() { - return client_image_registry_.get(); - } service_manager::Connector* connector() { return connector_; } CrosDisplayConfig* cros_display_config() { return cros_display_config_.get(); @@ -740,7 +736,6 @@ std::unique_ptr<BacklightsForcedOffSetter> backlights_forced_off_setter_; std::unique_ptr<BrightnessControlDelegate> brightness_control_delegate_; std::unique_ptr<CastConfigController> cast_config_; - std::unique_ptr<ClientImageRegistry> client_image_registry_; std::unique_ptr<CrosDisplayConfig> cros_display_config_; service_manager::Connector* const connector_; std::unique_ptr<DetachableBaseHandler> detachable_base_handler_;
diff --git a/ash/system/accessibility/dictation_button_tray.cc b/ash/system/accessibility/dictation_button_tray.cc index f133342b..738a22f 100644 --- a/ash/system/accessibility/dictation_button_tray.cc +++ b/ash/system/accessibility/dictation_button_tray.cc
@@ -6,6 +6,7 @@ #include "ash/accessibility/accessibility_controller.h" #include "ash/metrics/user_metrics_recorder.h" +#include "ash/public/interfaces/accessibility_controller.mojom.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/shelf/shelf_constants.h" #include "ash/shell.h" @@ -46,11 +47,9 @@ } bool DictationButtonTray::PerformAction(const ui::Event& event) { - UserMetricsRecorder::RecordUserToggleDictation( - DictationToggleMethod::kToggleByButton); + Shell::Get()->accessibility_controller()->ToggleDictationFromSource( + mojom::DictationToggleSource::kButton); - Shell::Get()->accelerator_controller()->PerformActionIfEnabled( - AcceleratorAction::TOGGLE_DICTATION); CheckDictationStatusAndUpdateIcon(); return true; }
diff --git a/ash/wm/non_client_frame_controller.cc b/ash/wm/non_client_frame_controller.cc index 6e7f171..4f07510 100644 --- a/ash/wm/non_client_frame_controller.cc +++ b/ash/wm/non_client_frame_controller.cc
@@ -81,15 +81,13 @@ class WmNativeWidgetAura : public views::NativeWidgetAura { public: WmNativeWidgetAura(views::internal::NativeWidgetDelegate* delegate, - bool remove_standard_frame, - bool should_ash_control_immersive) + bool remove_standard_frame) // The NativeWidget is mirroring the real Widget created in client code. // |is_parallel_widget_in_window_manager| is used to indicate this : views::NativeWidgetAura(delegate, true /* is_parallel_widget_in_window_manager */, Shell::Get()->aura_env()), - remove_standard_frame_(remove_standard_frame), - should_ash_control_immersive_(should_ash_control_immersive) {} + remove_standard_frame_(remove_standard_frame) {} ~WmNativeWidgetAura() override = default; void set_cursor(const ui::Cursor& cursor) { cursor_ = cursor; } @@ -98,12 +96,8 @@ views::NonClientFrameView* CreateNonClientFrameView() override { // TODO(sky): investigate why we have this. Seems this should be the same // as not specifying client area insets. - if (remove_standard_frame_) - return new EmptyDraggableNonClientFrameView(); - aura::Window* window = GetNativeView(); - - if (!should_ash_control_immersive_) { - wm::InstallResizeHandleWindowTargeterForWindow(window); + if (remove_standard_frame_) { + wm::InstallResizeHandleWindowTargeterForWindow(GetNativeWindow()); return new EmptyDraggableNonClientFrameView(); } @@ -129,7 +123,6 @@ private: const bool remove_standard_frame_; - const bool should_ash_control_immersive_; // The cursor for this widget. CompoundEventFilter will retrieve this cursor // via GetCursor and update the CursorManager's active cursor as appropriate @@ -225,8 +218,7 @@ params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; params.layer_type = ui::LAYER_SOLID_COLOR; WmNativeWidgetAura* native_widget = - new WmNativeWidgetAura(widget_, ShouldRemoveStandardFrame(*properties), - ShouldEnableImmersive(*properties)); + new WmNativeWidgetAura(widget_, ShouldRemoveStandardFrame(*properties)); window_ = native_widget->GetNativeView(); window_->SetProperty(kNonClientFrameControllerKey, this); window_->SetProperty(kWidgetCreationTypeKey, WidgetCreationType::FOR_CLIENT);
diff --git a/ash/wm/property_util.cc b/ash/wm/property_util.cc index 1706b18..eeaba30 100644 --- a/ash/wm/property_util.cc +++ b/ash/wm/property_util.cc
@@ -57,12 +57,6 @@ return iter != properties.end() && mojo::ConvertTo<bool>(iter->second); } -bool ShouldEnableImmersive(const InitProperties& properties) { - auto iter = - properties.find(ws::mojom::WindowManager::kDisableImmersive_InitProperty); - return iter == properties.end() || !mojo::ConvertTo<bool>(iter->second); -} - void ApplyProperties( aura::Window* window, aura::PropertyConverter* property_converter,
diff --git a/ash/wm/property_util.h b/ash/wm/property_util.h index 40cec91..3729c3f42c 100644 --- a/ash/wm/property_util.h +++ b/ash/wm/property_util.h
@@ -55,8 +55,6 @@ bool ShouldRemoveStandardFrame(const InitProperties& properties); -bool ShouldEnableImmersive(const InitProperties& properties); - // Applies |properties| to |window| using |property_converter|. void ApplyProperties( aura::Window* window,
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc index 14c3b46f..51970b4 100644 --- a/ash/wm/splitview/split_view_controller_unittest.cc +++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -2814,6 +2814,9 @@ EXPECT_TRUE(window3->IsVisible()); EXPECT_TRUE(window4->IsVisible()); + if (Shell::Get()->app_list_controller()->IsHomeLauncherEnabledInTabletMode()) + EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible()); + // 1) Start dragging |window1|. |window2| is the source window. std::unique_ptr<WindowResizer> resizer = StartDrag(window1.get(), window2.get());
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc index 3501f553..9e0b984 100644 --- a/ash/wm/toplevel_window_event_handler.cc +++ b/ash/wm/toplevel_window_event_handler.cc
@@ -52,8 +52,7 @@ : ::wm::WINDOW_MOVE_SOURCE_MOUSE; if (gesture_target) { window->env()->gesture_recognizer()->TransferEventsTo( - gesture_target, window, - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + gesture_target, window, ui::TransferTouchesBehavior::kDontCancel); } return wm_toplevel_window_event_handler_.AttemptToStartDrag( window, point_in_parent, window_component, source,
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index 7f1f85f..fdb039a 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc
@@ -97,8 +97,10 @@ bool ShouldUseExtendedBounds(const aura::Window* target) const override { // Fullscreen/maximized windows can't be drag-resized. - if (GetWindowState(window())->IsMaximizedOrFullscreenOrPinned()) + if (GetWindowState(window())->IsMaximizedOrFullscreenOrPinned() || + !wm::GetWindowState(target)->CanResize()) { return false; + } // The shrunken hit region only applies to children of |window()|. return target->parent() == window();
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc index f835953..2ef690a 100644 --- a/base/allocator/partition_allocator/partition_bucket.cc +++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -478,11 +478,7 @@ PartitionExcessiveAllocationSize(); } new_page = PartitionDirectMap(root, flags, size); -#if !defined(OS_MACOSX) - // TODO(https://crbug.com/890752): Remove this when we figure out and fix - // whatever is breaking on macOS. *is_already_zeroed = true; -#endif } else if (LIKELY(this->SetNewActivePage())) { // First, did we find an active page in the active pages list? new_page = this->active_pages_head; @@ -514,11 +510,9 @@ void* addr = PartitionPage::ToPointer(new_page); root->RecommitSystemPages(addr, new_page->bucket->get_bytes_per_span()); new_page->Reset(); -#if !defined(OS_MACOSX) - // TODO(https://crbug.com/890752): Remove this when we figure out and fix - // whatever is breaking on macOS. - *is_already_zeroed = true; -#endif + // TODO(https://crbug.com/890752): Optimizing here might cause pages to + // not be zeroed. + // *is_already_zeroed = true; } DCHECK(new_page); } else { @@ -528,11 +522,9 @@ if (LIKELY(raw_pages != nullptr)) { new_page = PartitionPage::FromPointerNoAlignmentCheck(raw_pages); InitializeSlotSpan(new_page); -#if !defined(OS_MACOSX) - // TODO(https://crbug.com/890752): Remove this when we figure out and fix - // whatever is breaking on macOS. - *is_already_zeroed = true; -#endif + // TODO(https://crbug.com/890752): Optimizing here causes pages to not be + // zeroed on at least macOS. + // *is_already_zeroed = true; } }
diff --git a/base/allocator/partition_allocator/partition_root_base.h b/base/allocator/partition_allocator/partition_root_base.h index e1392d0..3ab3be8 100644 --- a/base/allocator/partition_allocator/partition_root_base.h +++ b/base/allocator/partition_allocator/partition_root_base.h
@@ -142,11 +142,6 @@ } PartitionCookieWriteValue(char_ret + kCookieSize + no_cookie_size); #else -#if defined(OS_MACOSX) - // TODO(https://crbug.com/890752): Remove this when we figure out and fix - // whatever is breaking on macOS. - is_already_zeroed = false; -#endif if (ret && zero_fill && !is_already_zeroed) { memset(ret, 0, size); }
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc index e31a641..48a3a19 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -120,6 +120,15 @@ // Accumulated bytes towards sample thread local key. TLSKey g_accumulated_bytes_tls; +// A boolean used to distinguish first allocation on a thread. +// false - first allocation on the thread. +// true - otherwise +// Since g_accumulated_bytes_tls is initialized with zero the very first +// allocation on a thread would always trigger the sample, thus skewing the +// profile towards such allocations. To mitigate that we use the flag to +// ensure the first allocation is properly accounted. +TLSKey g_sampling_interval_initialized_tls; + // Controls if sample intervals should not be randomized. Used for testing. bool g_deterministic; @@ -294,6 +303,7 @@ ReentryGuard::Init(); TLSInit(&g_internal_reentry_guard); TLSInit(&g_accumulated_bytes_tls); + TLSInit(&g_sampling_interval_initialized_tls); return true; }(); ignore_result(init_once); @@ -411,6 +421,16 @@ TLSSetValue(g_accumulated_bytes_tls, accumulated_bytes); + if (UNLIKELY(!TLSGetValue(g_sampling_interval_initialized_tls))) { + TLSSetValue(g_sampling_interval_initialized_tls, true); + // This is the very first allocation on the thread. It always produces an + // extra sample because g_accumulated_bytes_tls is initialized with zero + // due to TLS semantics. + // Make sure we don't count this extra sample. + if (!--samples) + return; + } + if (UNLIKELY(TLSGetValue(g_internal_reentry_guard))) return;
diff --git a/base/task/task_traits.h b/base/task/task_traits.h index aafd1187..3b5994f 100644 --- a/base/task/task_traits.h +++ b/base/task/task_traits.h
@@ -123,15 +123,24 @@ // Describes immutable metadata for a single task or a group of tasks. class BASE_EXPORT TaskTraits { private: - // ValidTrait ensures TaskTraits' constructor only accepts appropriate types. - struct ValidTrait { - ValidTrait(TaskPriority) {} - ValidTrait(TaskShutdownBehavior) {} - ValidTrait(MayBlock) {} - ValidTrait(WithBaseSyncPrimitives) {} - }; + using TaskPriorityFilter = + trait_helpers::EnumTraitFilter<TaskPriority, TaskPriority::USER_VISIBLE>; + using MayBlockFilter = trait_helpers::BooleanTraitFilter<MayBlock>; + using TaskShutdownBehaviorFilter = + trait_helpers::EnumTraitFilter<TaskShutdownBehavior, + TaskShutdownBehavior::SKIP_ON_SHUTDOWN>; + using WithBaseSyncPrimitivesFilter = + trait_helpers::BooleanTraitFilter<WithBaseSyncPrimitives>; public: + // ValidTrait ensures TaskTraits' constructor only accepts appropriate types. + struct ValidTrait { + ValidTrait(TaskPriority); + ValidTrait(TaskShutdownBehavior); + ValidTrait(MayBlock); + ValidTrait(WithBaseSyncPrimitives); + }; + // Invoking this constructor without arguments produces TaskTraits that are // appropriate for tasks that // (1) don't block (ref. MayBlock() and WithBaseSyncPrimitives()), @@ -153,49 +162,26 @@ // constexpr base::TaskTraits other_user_visible_may_block_traits = { // base::MayBlock(), base::TaskPriority::USER_VISIBLE}; template <class... ArgTypes, - class CheckArgumentsAreValidBaseTraits = std::enable_if_t< - trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> + class CheckArgumentsAreValid = std::enable_if_t< + trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value || + trait_helpers::AreValidTraitsForExtension<ArgTypes...>::value>> constexpr TaskTraits(ArgTypes... args) - : priority_(trait_helpers::GetValueFromArgList( - trait_helpers::EnumArgGetter<TaskPriority, - TaskPriority::USER_VISIBLE>(), + : extension_(trait_helpers::GetTaskTraitsExtension( + trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>{}, args...)), - shutdown_behavior_(trait_helpers::GetValueFromArgList( - trait_helpers::EnumArgGetter< - TaskShutdownBehavior, - TaskShutdownBehavior::SKIP_ON_SHUTDOWN>(), - args...)), + priority_( + trait_helpers::GetTraitFromArgList<TaskPriorityFilter>(args...)), + shutdown_behavior_( + trait_helpers::GetTraitFromArgList<TaskShutdownBehaviorFilter>( + args...)), priority_set_explicitly_( - trait_helpers::HasArgOfType<TaskPriority, ArgTypes...>::value), + trait_helpers::TraitIsDefined<TaskPriorityFilter>(args...)), shutdown_behavior_set_explicitly_( - trait_helpers::HasArgOfType<TaskShutdownBehavior, - ArgTypes...>::value), - may_block_(trait_helpers::GetValueFromArgList( - trait_helpers::BooleanArgGetter<MayBlock>(), - args...)), - with_base_sync_primitives_(trait_helpers::GetValueFromArgList( - trait_helpers::BooleanArgGetter<WithBaseSyncPrimitives>(), - args...)) {} - - // Construct TaskTraits with extension traits. See task_traits_extension.h. - template <class... ArgTypes, - class AvoidConstructorRedeclaration = void, - class CheckArgsContainNonBaseTrait = std::enable_if_t< - !trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> - constexpr TaskTraits(ArgTypes... args) - // Select those arguments that are valid base TaskTraits and forward them - // to the above constructor via a helper constructor. - : TaskTraits(std::forward_as_tuple(args...), - trait_helpers::SelectIndices< - trait_helpers::ValidTraitTester<ValidTrait>::IsValid, - ArgTypes...>{}) { - // Select all other arguments and try to create an extension with them. - extension_ = MakeTaskTraitsExtensionHelper( - std::forward_as_tuple(args...), - trait_helpers::SelectIndices< - trait_helpers::ValidTraitTester<ValidTrait>::IsInvalid, - ArgTypes...>{}); - } + trait_helpers::TraitIsDefined<TaskShutdownBehaviorFilter>(args...)), + may_block_(trait_helpers::GetTraitFromArgList<MayBlockFilter>(args...)), + with_base_sync_primitives_( + trait_helpers::GetTraitFromArgList<WithBaseSyncPrimitivesFilter>( + args...)) {} constexpr TaskTraits(const TaskTraits& other) = default; TaskTraits& operator=(const TaskTraits& other) = default; @@ -279,16 +265,6 @@ with_base_sync_primitives_(left.with_base_sync_primitives_ || right.with_base_sync_primitives_) {} - // Helper constructor which selects those arguments from |args| that are - // indicated by the index_sequence and forwards them to the public - // constructor. Due to filtering, the indices may be non-contiguous. - template <class... ArgTypes, std::size_t... Indices> - constexpr TaskTraits(std::tuple<ArgTypes...> args, - std::index_sequence<Indices...>) - : TaskTraits( - std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...) { - } - // Ordered for packing. TaskTraitsExtensionStorage extension_; TaskPriority priority_;
diff --git a/base/task/task_traits_details.h b/base/task/task_traits_details.h index 82d971c5..f9a9c5d 100644 --- a/base/task/task_traits_details.h +++ b/base/task/task_traits_details.h
@@ -5,6 +5,7 @@ #ifndef BASE_TASK_TASK_TRAITS_DETAILS_H_ #define BASE_TASK_TASK_TRAITS_DETAILS_H_ +#include <initializer_list> #include <tuple> #include <type_traits> #include <utility> @@ -12,116 +13,147 @@ namespace base { namespace trait_helpers { -// HasArgOfType<CheckedType, ArgTypes...>::value is true iff a type in ArgTypes -// matches CheckedType. -template <class...> -struct HasArgOfType : std::false_type {}; -template <class CheckedType, class FirstArgType, class... ArgTypes> -struct HasArgOfType<CheckedType, FirstArgType, ArgTypes...> - : std::conditional<std::is_same<CheckedType, FirstArgType>::value, - std::true_type, - HasArgOfType<CheckedType, ArgTypes...>>::type {}; +// Checks if any of the elements in |ilist| is true. +// Similar to std::any_of for the case of constexpr initializer_list. +inline constexpr bool any_of(std::initializer_list<bool> ilist) { + for (auto c : ilist) { + if (c) + return true; + } + return false; +} -// When the following call is made: -// GetValueFromArgListImpl(CallFirstTag(), GetterType(), args...); -// If |args| is empty, the compiler selects the first overload. This overload -// returns getter.GetDefaultValue(). If |args| is not empty, the compiler -// prefers using the second overload because the type of the first argument -// matches exactly. This overload returns getter.GetValueFromArg(first_arg), -// where |first_arg| is the first element in |args|. If -// getter.GetValueFromArg(first_arg) isn't defined, the compiler uses the third -// overload instead. This overload discards the first argument in |args| and -// makes a recursive call to GetValueFromArgListImpl() with CallFirstTag() as -// first argument. +// Checks if all of the elements in |ilist| are true. +// Similar to std::any_of for the case of constexpr initializer_list. +inline constexpr bool all_of(std::initializer_list<bool> ilist) { + for (auto c : ilist) { + if (!c) + return false; + } + return true; +} -// Tag dispatching. +// Counts the elements in |ilist| that are equal to |value|. +// Similar to std::count for the case of constexpr initializer_list. +template <class T> +inline constexpr size_t count(std::initializer_list<T> ilist, T value) { + size_t c = 0; + for (const auto& v : ilist) { + c += (v == value); + } + return c; +} + +// CallFirstTag is an argument tag that helps to avoid ambiguous overloaded +// functions. When the following call is made: +// func(CallFirstTag(), arg...); +// the compiler will give precedence to an overload candidate that directly +// takes CallFirstTag. Another overload that takes CallSecondTag will be +// considered iff the preferred overload candidates were all invalids and +// therefore discarded. struct CallSecondTag {}; struct CallFirstTag : CallSecondTag {}; -// Overload 1: Default value. -template <class GetterType> -constexpr typename GetterType::ValueType GetValueFromArgListImpl( - CallFirstTag, - GetterType getter) { - return getter.GetDefaultValue(); +// A trait filter class |TraitFilterType| implements the protocol to get a value +// of type |ArgType| from an argument list and convert it to a value of type +// |TraitType|. If the argument list contains an argument of type |ArgType|, the +// filter class will be instantiated with that argument. If the argument list +// contains no argument of type |ArgType|, the filter class will be instantiated +// using the default constructor if available; a compile error is issued +// otherwise. The filter class must have the conversion operator TraitType() +// which returns a value of type TraitType. + +// |InvalidTrait| is used to return from GetTraitFromArg when the argument is +// not compatible with the desired trait. +struct InvalidTrait {}; + +// Returns an object of type |TraitFilterType| constructed from |arg| if +// compatible, or |InvalidTrait| otherwise. +template <class TraitFilterType, + class ArgType, + class CheckArgumentIsCompatible = std::enable_if_t< + std::is_constructible<TraitFilterType, ArgType>::value>> +constexpr TraitFilterType GetTraitFromArg(CallFirstTag, ArgType arg) { + return TraitFilterType(arg); } -// Overload 2: Get value from first argument. Check that no argument in |args| -// has the same type as |first_arg|. -template <class GetterType, - class FirstArgType, +template <class TraitFilterType, class ArgType> +constexpr InvalidTrait GetTraitFromArg(CallSecondTag, ArgType arg) { + return InvalidTrait(); +} + +// Returns an object of type |TraitFilterType| constructed from a compatible +// argument in |args...|, or default constructed if none of the arguments are +// compatible. This is the implementation of GetTraitFromArgList() with a +// disambiguation tag. +template <class TraitFilterType, class... ArgTypes, - class TestGetValueFromArgDefined = - decltype(std::declval<GetterType>().GetValueFromArg( - std::declval<FirstArgType>()))> -constexpr typename GetterType::ValueType GetValueFromArgListImpl( - CallFirstTag, - GetterType getter, - const FirstArgType& first_arg, - const ArgTypes&... args) { - static_assert(!HasArgOfType<FirstArgType, ArgTypes...>::value, - "Multiple arguments of the same type were provided to the " - "constructor of TaskTraits."); - return getter.GetValueFromArg(first_arg); + class TestCompatibleArgument = std::enable_if_t<any_of( + {std::is_constructible<TraitFilterType, ArgTypes>::value...})>> +constexpr TraitFilterType GetTraitFromArgListImpl(CallFirstTag, + ArgTypes... args) { + return std::get<TraitFilterType>(std::make_tuple( + GetTraitFromArg<TraitFilterType>(CallFirstTag(), args)...)); } -// Overload 3: Discard first argument. -template <class GetterType, class FirstArgType, class... ArgTypes> -constexpr typename GetterType::ValueType GetValueFromArgListImpl( - CallSecondTag, - GetterType getter, - const FirstArgType&, - const ArgTypes&... args) { - return GetValueFromArgListImpl(CallFirstTag(), getter, args...); +template <class TraitFilterType, class... ArgTypes> +constexpr TraitFilterType GetTraitFromArgListImpl(CallSecondTag, + ArgTypes... args) { + static_assert(std::is_constructible<TraitFilterType>::value, + "TaskTraits contains a Trait that must be explicity " + "initialized in its constructor."); + return TraitFilterType(); } -// If there is an argument |arg_of_type| of type Getter::ArgType in |args|, -// returns getter.GetValueFromArg(arg_of_type). If there are more than one -// argument of type Getter::ArgType in |args|, generates a compile-time error. -// Otherwise, returns getter.GetDefaultValue(). -// -// |getter| must provide: -// ValueType: -// The return type of GetValueFromArgListImpl(). -// -// ArgType: -// The type of the argument from which GetValueFromArgListImpl() derives -// its return value. -// -// ValueType GetValueFromArg(ArgType): -// Converts an argument of type ArgType into a value returned by -// GetValueFromArgListImpl(). -// -// |getter| may provide: -// ValueType GetDefaultValue(): -// Returns the value returned by GetValueFromArgListImpl() if none of -// its arguments is of type ArgType. If this method is not provided, -// compilation will fail when no argument of type ArgType is provided. -template <class GetterType, class... ArgTypes> -constexpr typename GetterType::ValueType GetValueFromArgList( - GetterType getter, - const ArgTypes&... args) { - return GetValueFromArgListImpl(CallFirstTag(), getter, args...); +// Constructs an object of type |TraitFilterType| from a compatible argument in +// |args...|, or using the default constructor, and returns its associated trait +// value using conversion to |TraitFilterType::ValueType|. If there are more +// than one compatible argument in |args|, generates a compile-time error. +template <class TraitFilterType, class... ArgTypes> +constexpr typename TraitFilterType::ValueType GetTraitFromArgList( + ArgTypes... args) { + static_assert( + count({std::is_constructible<TraitFilterType, ArgTypes>::value...}, + true) <= 1, + "Multiple arguments of the same type were provided to the " + "constructor of TaskTraits."); + return GetTraitFromArgListImpl<TraitFilterType>(CallFirstTag(), args...); } +// Returns true if this trait is explicitly defined in an argument list, i.e. +// there is an argument compatible with this trait in |args...|. +template <class TraitFilterType, class... ArgTypes> +constexpr bool TraitIsDefined(ArgTypes... args) { + return any_of({std::is_constructible<TraitFilterType, ArgTypes>::value...}); +} + +// Helper class to implemnent a |TraitFilterType|. +template <typename T> +struct BasicTraitFilter { + using ValueType = T; + + constexpr operator ValueType() const { return value; } + + ValueType value = {}; +}; + template <typename ArgType> -struct BooleanArgGetter { - using ValueType = bool; - constexpr ValueType GetValueFromArg(ArgType) const { return true; } - constexpr ValueType GetDefaultValue() const { return false; } +struct BooleanTraitFilter : public BasicTraitFilter<bool> { + constexpr BooleanTraitFilter() { this->value = false; } + constexpr BooleanTraitFilter(ArgType) { this->value = true; } }; template <typename ArgType, ArgType DefaultValue> -struct EnumArgGetter { - using ValueType = ArgType; - constexpr ValueType GetValueFromArg(ArgType arg) const { return arg; } - constexpr ValueType GetDefaultValue() const { return DefaultValue; } +struct EnumTraitFilter : public BasicTraitFilter<ArgType> { + constexpr EnumTraitFilter() { this->value = DefaultValue; } + constexpr EnumTraitFilter(ArgType arg) { this->value = arg; } }; +// Tests whether multiple given argtument types are all valid traits according +// to the provided ValidTraits. To use, define a ValidTraits template <typename ArgType> -struct RequiredEnumArgGetter { - using ValueType = ArgType; - constexpr ValueType GetValueFromArg(ArgType arg) const { return arg; } +struct RequiredEnumTraitFilter : public BasicTraitFilter<ArgType> { + constexpr RequiredEnumTraitFilter(ArgType arg) { this->value = arg; } }; // Tests whether a given trait type is valid or invalid by testing whether it is @@ -129,82 +161,14 @@ // type like this: // // struct ValidTraits { -// ValidTraits(MyTrait) {} +// ValidTraits(MyTrait); // ... // }; -template <class ValidTraits> -struct ValidTraitTester { - template <class TraitType> - struct IsValid : std::is_convertible<TraitType, ValidTraits> {}; - - template <class TraitType> - struct IsInvalid - : std::conditional_t<std::is_convertible<TraitType, ValidTraits>::value, - std::false_type, - std::true_type> {}; -}; - -// Tests if a given trait type is valid according to the provided ValidTraits. -template <class ValidTraits, class TraitType> -struct IsValidTrait - : ValidTraitTester<ValidTraits>::template IsValid<TraitType> {}; - -// Tests whether multiple given traits types are all valid according to the -// provided ValidTraits. -template <class ValidTraits, class...> -struct AreValidTraits : std::true_type {}; - -template <class ValidTraits, class NextType, class... Rest> -struct AreValidTraits<ValidTraits, NextType, Rest...> - : std::conditional<IsValidTrait<ValidTraits, NextType>::value, - AreValidTraits<ValidTraits, Rest...>, - std::false_type>::type {}; - -// Helper struct that recursively builds up an index_sequence containing all -// those indexes of elements in Args for which the |Predicate<Arg>::value| is -// true. -template <template <class> class Predicate, - std::size_t CurrentIndex, - class Output, - class... Args> -struct SelectIndicesHelper; - -template <template <class> class Predicate, - std::size_t CurrentIndex, - std::size_t... Indices, - class First, - class... Rest> -struct SelectIndicesHelper<Predicate, - CurrentIndex, - std::index_sequence<Indices...>, - First, - Rest...> - : std::conditional_t< - Predicate<First>::value, - // Push the index into the sequence and recurse. - SelectIndicesHelper<Predicate, - CurrentIndex + 1, - std::index_sequence<Indices..., CurrentIndex>, - Rest...>, - // Skip the index and recurse. - SelectIndicesHelper<Predicate, - CurrentIndex + 1, - std::index_sequence<Indices...>, - Rest...>> {}; - -template <template <class> class Predicate, - std::size_t CurrentIndex, - class Sequence> -struct SelectIndicesHelper<Predicate, CurrentIndex, Sequence> { - using type = Sequence; -}; - -// Selects the indices of elements in the |Args| list for which -// |Predicate<Arg>::value| is |true|. -template <template <class> class Predicate, class... Args> -using SelectIndices = - typename SelectIndicesHelper<Predicate, 0, std::index_sequence<>, Args...>:: - type; +template <class ValidTraits, class... ArgTypes> +struct AreValidTraits + : std::integral_constant< + bool, + all_of({std::is_convertible<ArgTypes, ValidTraits>::value...})> {}; } // namespace trait_helpers } // namespace base
diff --git a/base/task/task_traits_extension.h b/base/task/task_traits_extension.h index ad381883..b90625a 100644 --- a/base/task/task_traits_extension.h +++ b/base/task/task_traits_extension.h
@@ -12,6 +12,7 @@ #include <utility> #include "base/base_export.h" +#include "base/task/task_traits_details.h" namespace base { @@ -41,10 +42,14 @@ // as its extension traits: // (3) -- template <...> // -- constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( -// -- ArgTypes&&... args). +// -- ArgTypes... args). // Constructs and serializes an extension with the given arguments into -// a TaskTraitsExtensionStorage and returns it. Should only accept valid -// arguments for the extension. +// a TaskTraitsExtensionStorage and returns it. When the extension is used, +// all traits, including the base ones, are passed to this function in +// order make sure TaskTraits constructor only participates in overload +// resolution if all traits are valid. As such, this function should only +// accept valid task traits recognised by the extension and the base task +// traits. // // EXAMPLE (see also base/task/test_task_traits_extension.h): // -------- @@ -57,20 +62,24 @@ // static constexpr uint8_t kExtensionId = // TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; // -// struct ValidTrait { -// ValidTrait(MyExtensionTrait) {} +// struct ValidTrait : public TaskTraits::ValidTrait { +// // Accept base traits in MakeTaskTraitsExtension (see above). +// using TaskTraits::ValidTrait::ValidTrait; +// +// ValidTrait(MyExtensionTrait); // }; // +// using MyExtensionTraitFilter = +// trait_helpers::EnumTraitFilter<MyExtensionTrait, MyExtensionTrait::kA>; +// // // Constructor that accepts only valid traits as specified by ValidTraits. // template <class... ArgTypes, // class CheckArgumentsAreValid = std::enable_if_t< // base::trait_helpers::AreValidTraits< // ValidTrait, ArgTypes...>::value>> // constexpr MyTaskTraitsExtension(ArgTypes... args) -// : my_trait_(base::trait_helpers::GetValueFromArgList( -// base::trait_helpers::EnumArgGetter<MyExtensionTrait, -// MyExtensionTrait::kValue1>(), -// args...)) {} +// : my_trait_(trait_helpers::GetTraitFromArgList<MyExtensionTraitFilter>( +// args...)) {} // // // Serializes MyTaskTraitsExtension into a storage object and returns it. // constexpr base::TaskTraitsExtensionStorage Serialize() const { @@ -100,8 +109,8 @@ // base::trait_helpers::AreValidTraits< // MyTaskTraitsExtension::ValidTrait, ArgTypes...>::value>> // constexpr base::TaskTraitsExtensionStorage MakeTaskTraitsExtension( -// ArgTypes&&... args) { -// return MyTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize(); +// ArgTypes... args) { +// return MyTaskTraitsExtension(args...).Serialize(); // } // } // namespace my_embedder // @@ -171,6 +180,44 @@ inline constexpr TaskTraitsExtensionStorage::TaskTraitsExtensionStorage( const TaskTraitsExtensionStorage& other) = default; +namespace trait_helpers { + +// Helper class whose constructor tests if an extension accepts a list of +// argument types. +struct TaskTraitsExtension { + template <class... ArgTypes, + class CheckCanMakeExtension = + decltype(MakeTaskTraitsExtension(std::declval<ArgTypes>()...))> + constexpr TaskTraitsExtension(ArgTypes... args) {} +}; + +// Tests that that a trait extension accepts all |ArgsTypes...|. +template <class... ArgTypes> +struct AreValidTraitsForExtension + : std::integral_constant< + bool, + std::is_constructible<TaskTraitsExtension, ArgTypes...>::value> {}; + +// Helper function that returns the TaskTraitsExtensionStorage of a +// serialized extension created with |args...| if there are arguments that are +// not valid base traits, or a default constructed TaskTraitsExtensionStorage +// otherwise. +template <class... ArgTypes> +constexpr TaskTraitsExtensionStorage GetTaskTraitsExtension( + std::true_type base_traits, + ArgTypes... args) { + return TaskTraitsExtensionStorage(); +} + +template <class... ArgTypes> +constexpr TaskTraitsExtensionStorage GetTaskTraitsExtension( + std::false_type base_traits, + ArgTypes... args) { + return MakeTaskTraitsExtension(args...); +} + +} // namespace trait_helpers + // TODO(eseckler): Default the comparison operator once C++20 arrives. inline bool TaskTraitsExtensionStorage::operator==( const TaskTraitsExtensionStorage& other) const { @@ -180,25 +227,6 @@ return extension_id == other.extension_id && data == other.data; } -// Default implementation of MakeTaskTraitsExtension template function, which -// doesn't accept any traits and does not create an extension. -template <class Unused = void> -constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension( - TaskTraitsExtensionStorage& storage) { - return TaskTraitsExtensionStorage(); -} - -// Forwards those arguments from |args| that are indicated by the index_sequence -// to a MakeTaskTraitsExtension() template function, which is provided by the -// embedder in an unknown namespace; its resolution relies on argument-dependent -// lookup. Due to filtering, the provided indices may be non-contiguous. -template <class... ArgTypes, std::size_t... Indices> -constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtensionHelper( - std::tuple<ArgTypes...> args, - std::index_sequence<Indices...>) { - return MakeTaskTraitsExtension( - std::get<Indices>(std::forward<std::tuple<ArgTypes...>>(args))...); -} } // namespace base
diff --git a/base/task/task_traits_extension_unittest.cc b/base/task/task_traits_extension_unittest.cc index 39033aa..12de5c6 100644 --- a/base/task/task_traits_extension_unittest.cc +++ b/base/task/task_traits_extension_unittest.cc
@@ -9,6 +9,13 @@ namespace base { +TEST(TaskTraitsExtensionTest, NoExtension) { + constexpr TaskTraits traits = {}; + + EXPECT_EQ(traits.extension_id(), + TaskTraitsExtensionStorage::kInvalidExtensionId); +} + TEST(TaskTraitsExtensionTest, CreateWithOneExtensionTrait) { constexpr TaskTraits traits = {TestExtensionEnumTrait::kB};
diff --git a/base/task/task_traits_extension_unittest.nc b/base/task/task_traits_extension_unittest.nc index aa74c33..739e255 100644 --- a/base/task/task_traits_extension_unittest.nc +++ b/base/task/task_traits_extension_unittest.nc
@@ -15,7 +15,7 @@ constexpr TaskTraits traits = {MayBlock(), MayBlock()}; #elif defined(NCTEST_TASK_TRAITS_EXTENSION_MULTIPLE_EXTENSION_TRAITS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."] constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, TestExtensionEnumTrait::kC}; -#elif defined(NCTEST_TASK_TRAITS_EXTENSION_INVALID_TYPE) // [r"no matching function for call to 'MakeTaskTraitsExtension'"] +#elif defined(NCTEST_TASK_TRAITS_EXTENSION_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"] constexpr TaskTraits traits = {TestExtensionEnumTrait::kB, 123}; #elif defined(NCTEST_TASK_TRAITS_EXTENSION_TOO_MUCH_DATA_FOR_STORAGE) // [r"no matching constructor for initialization of 'base::TaskTraitsExtensionStorage'"] constexpr TaskTraitsExtensionStorage TestSerializeTaskTraitsWithTooMuchData() {
diff --git a/base/task/task_traits_unittest.nc b/base/task/task_traits_unittest.nc index 8eaafa2..aa13b54 100644 --- a/base/task/task_traits_unittest.nc +++ b/base/task/task_traits_unittest.nc
@@ -24,7 +24,7 @@ constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, MayBlock(), TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; -#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching function for call to 'MakeTaskTraitsExtension'"] +#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"] constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true}; #endif
diff --git a/base/task/test_task_traits_extension.h b/base/task/test_task_traits_extension.h index ec12361..3baed34b 100644 --- a/base/task/test_task_traits_extension.h +++ b/base/task/test_task_traits_extension.h
@@ -20,21 +20,26 @@ static constexpr uint8_t kExtensionId = TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; - struct ValidTrait { - ValidTrait(TestExtensionEnumTrait) {} - ValidTrait(TestExtensionBoolTrait) {} + struct ValidTrait : public TaskTraits::ValidTrait { + using TaskTraits::ValidTrait::ValidTrait; + + ValidTrait(TestExtensionEnumTrait); + ValidTrait(TestExtensionBoolTrait); }; + using TestExtensionEnumFilter = + trait_helpers::EnumTraitFilter<TestExtensionEnumTrait, + TestExtensionEnumTrait::kA>; + using TestExtensionBoolFilter = + trait_helpers::BooleanTraitFilter<TestExtensionBoolTrait>; + template <class... ArgTypes, class CheckArgumentsAreValid = std::enable_if_t< trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> constexpr TestTaskTraitsExtension(ArgTypes... args) - : enum_trait_(trait_helpers::GetValueFromArgList( - trait_helpers::EnumArgGetter<TestExtensionEnumTrait, - TestExtensionEnumTrait::kA>(), + : enum_trait_(trait_helpers::GetTraitFromArgList<TestExtensionEnumFilter>( args...)), - bool_trait_(trait_helpers::GetValueFromArgList( - trait_helpers::BooleanArgGetter<TestExtensionBoolTrait>(), + bool_trait_(trait_helpers::GetTraitFromArgList<TestExtensionBoolFilter>( args...)) {} constexpr TaskTraitsExtensionStorage Serialize() const { @@ -65,8 +70,7 @@ class = std::enable_if_t< trait_helpers::AreValidTraits<TestTaskTraitsExtension::ValidTrait, ArgTypes...>::value>> -constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension( - ArgTypes&&... args) { +constexpr TaskTraitsExtensionStorage MakeTaskTraitsExtension(ArgTypes... args) { return TestTaskTraitsExtension(std::forward<ArgTypes>(args)...).Serialize(); }
diff --git a/build/config/fuchsia/fidl_library.gni b/build/config/fuchsia/fidl_library.gni index 6fbb28b6..5ec90a49 100644 --- a/build/config/fuchsia/fidl_library.gni +++ b/build/config/fuchsia/fidl_library.gni
@@ -14,12 +14,14 @@ # namespace - (optional) Namespace for the library. # deps - (optional) List of other fidl_library() targets that this # FIDL library depends on. +# languages - Generate bindings for the given languages, defaults to +# [ "cpp" ]. "js" also supported. # # $namespace.$library_name must match the the library name specified in the FIDL # files. template("fidl_library") { - forward_variables_from(invoker, [ "namespace" ]) + forward_variables_from(invoker, [ "namespace", "languages" ]) _library_basename = target_name if (defined(invoker.library_name)) { @@ -35,6 +37,10 @@ _library_path = _library_basename } + if (!defined(invoker.languages)) { + languages = [ "cpp" ] + } + _response_file = "$target_gen_dir/$target_name.rsp" _json_representation = "$target_gen_dir/${_library_name}.fidl.json" _output_gen_dir = "$target_gen_dir/fidl" @@ -131,6 +137,7 @@ action("${target_name}_cpp_gen") { visibility = [ ":${invoker.target_name}" ] + forward_variables_from(invoker, [ "testonly" ]) deps = [ ":${invoker.target_name}_compile", @@ -162,6 +169,40 @@ ] } + _output_js_path = "$_output_gen_dir/${_library_path}/js/fidl.js" + action("${target_name}_js_gen") { + visibility = [ ":${invoker.target_name}" ] + forward_variables_from(invoker, [ "testonly" ]) + + deps = [ + ":${invoker.target_name}_compile", + ] + + inputs = [ + # Depend on the SDK hash, to ensure rebuild if the SDK tools change. + rebase_path("$fuchsia_sdk/.hash"), + _json_representation, + "//tools/fuchsia/fidlgen_js/fidl.py", # The schema helper file. + ] + + outputs = [ + _output_js_path, + ] + + script = "//tools/fuchsia/fidlgen_js/gen.py" + + args = [ + rebase_path(_json_representation), + "--output", + rebase_path("${_output_js_path}"), + ] + + data = [] + foreach(o, outputs) { + data += [ rebase_path(o) ] + } + } + config("${target_name}_config") { visibility = [ ":${invoker.target_name}" ] include_dirs = [ _output_gen_dir ] @@ -185,10 +226,11 @@ if (!defined(deps)) { deps = [] } - deps += [ - ":${invoker.target_name}_compile", - ":${invoker.target_name}_cpp_gen", - ] + deps += [ ":${invoker.target_name}_compile" ] + + foreach(language, languages) { + deps += [ ":${invoker.target_name}_${language}_gen" ] + } if (!defined(public_deps)) { public_deps = []
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java index fb811a5..5be316ce 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -123,9 +123,10 @@ private static Configuration createConfiguration() { return new Configuration.Builder() - .put(ConfigKey.FEED_SERVER_HOST, "https://www.google.com") - .put(ConfigKey.FEED_SERVER_PATH_AND_PARAMS, - "/httpservice/noretry/NowStreamService/FeedQuery") + .put(ConfigKey.FEED_SERVER_ENDPOINT, FeedConfiguration.getFeedServerEndpoint()) + .put(ConfigKey.FEED_SERVER_METHOD, FeedConfiguration.getFeedServerEndpoint()) + .put(ConfigKey.FEED_SERVER_RESPONSE_LENGTH_PREFIXED, + FeedConfiguration.getFeedServerReponseLengthPrefixed()) .put(ConfigKey.SESSION_LIFETIME_MS, FeedConfiguration.getSessionLifetimeMs()) .put(ConfigKey.VIEW_LOG_THRESHOLD, FeedConfiguration.getViewLogThreshold()) .put(ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS,
diff --git a/chrome/android/java/res/layout/explore_sites_category_card_view.xml b/chrome/android/java/res/layout/explore_sites_category_card_view.xml index 210452c2..d4eb720c4 100644 --- a/chrome/android/java/res/layout/explore_sites_category_card_view.xml +++ b/chrome/android/java/res/layout/explore_sites_category_card_view.xml
@@ -8,8 +8,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center_horizontal" android:orientation="vertical" - android:gravity="center_horizontal" > + android:paddingVertical="@dimen/explore_sites_category_padding" > + <TextView android:id="@+id/category_title" @@ -20,10 +22,11 @@ <GridLayout android:id="@+id/category_sites" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" android:orientation="horizontal" android:columnCount="4" - android:rowCount="2" /> + android:columnOrderPreserved="true" /> </org.chromium.chrome.browser.explore_sites.ExploreSitesCategoryCardView>
diff --git a/chrome/android/java/res/layout/explore_sites_page_layout.xml b/chrome/android/java/res/layout/explore_sites_page_layout.xml index 0847921..e9255e41 100644 --- a/chrome/android/java/res/layout/explore_sites_page_layout.xml +++ b/chrome/android/java/res/layout/explore_sites_page_layout.xml
@@ -8,6 +8,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/explore_sites_category_recycler" android:scrollbars="vertical" - android:gravity="center_horizontal" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="wrap_content" + android:padding="@dimen/explore_sites_page_padding" />
diff --git a/chrome/android/java/res/layout/explore_sites_tile_view.xml b/chrome/android/java/res/layout/explore_sites_tile_view.xml index 664599d..8ae3cd9 100644 --- a/chrome/android/java/res/layout/explore_sites_tile_view.xml +++ b/chrome/android/java/res/layout/explore_sites_tile_view.xml
@@ -7,7 +7,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="@dimen/tile_view_width" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" > <include layout="@layout/tile_view_modern" android:layout_width="match_parent"
diff --git a/chrome/android/java/res/layout/explore_sites_title_card.xml b/chrome/android/java/res/layout/explore_sites_title_card.xml new file mode 100644 index 0000000..39febb15 --- /dev/null +++ b/chrome/android/java/res/layout/explore_sites_title_card.xml
@@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2018 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + + <TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:textAppearance="@style/BlackHeadline1" + android:text="@string/explore_sites_title" /> +
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 042e90c..8d194b9 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -350,6 +350,10 @@ <dimen name="md_incognito_ntp_line_spacing">6sp</dimen> <dimen name="md_incognito_ntp_padding_left">16dp</dimen> + <!-- Explore sites page --> + <dimen name="explore_sites_category_padding">12dp</dimen> + <dimen name="explore_sites_page_padding">12dp</dimen> + <!-- Recent tabs page --> <dimen name="recent_tabs_visible_separator_padding">8dp</dimen> <dimen name="recent_tabs_group_view_vertical_padding">8dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java index 6388f51..40d1ee98 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -7,8 +7,6 @@ import android.content.Context; import android.content.pm.ResolveInfo; import android.content.res.Resources; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; import android.os.SystemClock; import android.support.annotation.Nullable; import android.text.TextUtils; @@ -17,7 +15,6 @@ import android.view.View; import android.view.View.OnClickListener; -import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.metrics.RecordHistogram; @@ -143,14 +140,6 @@ if (offlineMenuItem != null) { offlineMenuItem.setEnabled( DownloadUtils.isAllowedToDownloadPage(currentTab)); - - Drawable drawable = offlineMenuItem.getIcon(); - if (drawable != null) { - int iconTint = ApiCompatibilityUtils.getColor( - mActivity.getResources(), R.color.default_icon_color); - drawable.mutate(); - drawable.setColorFilter(iconTint, PorterDuff.Mode.SRC_ATOP); - } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java index d6c633f..4c53444 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
@@ -21,6 +21,7 @@ import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.resources.ResourceManager; import org.chromium.ui.resources.dynamics.BitmapDynamicResource; import org.chromium.ui.resources.dynamics.DynamicResourceLoader; @@ -121,8 +122,9 @@ private String getUpdatedTitleInternal(Tab tab, String titleString, boolean fetchFaviconFromHistory) { final int tabId = tab.getId(); - boolean isDarkTheme = tab.isIncognito() - && !ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID); + boolean isHTSEnabled = !DeviceFormFactor.isNonMultiDisplayContextOnTablet(tab.getActivity()) + && ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID); + boolean isDarkTheme = tab.isIncognito() && !isHTSEnabled; Bitmap originalFavicon = tab.getFavicon(); if (originalFavicon == null) { originalFavicon = mDefaultFaviconHelper.getDefaultFaviconBitmap(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java index 127a75f..753d15a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardAdapter.java
@@ -7,9 +7,7 @@ import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; -import android.widget.TextView; -import org.chromium.chrome.R; import org.chromium.chrome.browser.modelutil.ForwardingListObservable; import org.chromium.chrome.browser.modelutil.PropertyKey; import org.chromium.chrome.browser.modelutil.PropertyModel; @@ -87,14 +85,7 @@ @Override public void onBindViewHolder(CategoryCardViewHolderFactory.CategoryCardViewHolder holder, int position, @Nullable Void payload) { - if (holder.getItemViewType() == ViewType.HEADER) { - TextView view = (TextView) holder.itemView; - view.setText(R.string.explore_sites_title); - } else if (holder.getItemViewType() == ViewType.ERROR) { - // populate the error view - } else if (holder.getItemViewType() == ViewType.LOADING) { - // Populate loading view - } else { + if (holder.getItemViewType() == ViewType.CATEGORY) { ExploreSitesCategoryCardView view = (ExploreSitesCategoryCardView) holder.itemView; // Position - 1 because there is always title. view.setCategory(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java index b62b3b7b..223babc 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/CategoryCardViewHolderFactory.java
@@ -29,12 +29,14 @@ View view; switch (viewType) { case CategoryCardAdapter.ViewType.HEADER: - TextView titleView = new TextView(parent.getContext()); - view = new TextView(parent.getContext()); + view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.explore_sites_title_card, parent, + /* attachToRoot = */ false); break; case CategoryCardAdapter.ViewType.CATEGORY: - view = (ExploreSitesCategoryCardView) LayoutInflater.from(parent.getContext()) - .inflate(R.layout.explore_sites_category_card_view, null); + view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.explore_sites_category_card_view, parent, + /* attachToRoot = */ false); break; case CategoryCardAdapter.ViewType.LOADING: // inflate loading spinny case CategoryCardAdapter.ViewType.ERROR: // inflate error @@ -46,4 +48,4 @@ } return new CategoryCardViewHolder(view); } -} \ No newline at end of file +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java index ec7e27a..298ba35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesCategoryCardView.java
@@ -29,6 +29,8 @@ * View for a category name and site tiles. */ public class ExploreSitesCategoryCardView extends LinearLayout { + private static final int MAX_TILE_COUNT = 8; + private TextView mTitleView; private GridLayout mTileView; private RoundedIconGenerator mIconGenerator; @@ -107,19 +109,25 @@ } public void updateTileViews(List<ExploreSitesSite> sites) { - // Remove extra views if too many. + // Remove extra tiles if too many. if (mTileView.getChildCount() > sites.size()) { mTileView.removeViews(sites.size(), mTileView.getChildCount() - sites.size()); } - // Add views if too few. - if (mTileView.getChildCount() < sites.size()) { - for (int i = mTileView.getChildCount(); i < sites.size(); i++) { + + // Maximum number of sites to show. + int tileMax = Math.min(MAX_TILE_COUNT, sites.size()); + + // Add tiles if too few + if (mTileView.getChildCount() < tileMax) { + for (int i = mTileView.getChildCount(); i < tileMax; i++) { mTileView.addView(LayoutInflater.from(getContext()) - .inflate(R.layout.explore_sites_tile_view, null)); + .inflate(R.layout.explore_sites_tile_view, mTileView, + /* attachToRoot = */ false)); } } - // Initialize all the tiles again to update. - for (int i = 0; i < sites.size(); i++) { + + // Initialize all the non-empty tiles again to update. + for (int i = 0; i < tileMax; i++) { ExploreSitesTileView tileView = (ExploreSitesTileView) mTileView.getChildAt(i); final ExploreSitesSite site = sites.get(i); tileView.initialize(site, mIconGenerator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java index d715ceb..9f79a01 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageNavigationDelegateImpl.java
@@ -26,6 +26,7 @@ * {@link NativePageNavigationDelegate} implementation. */ public class NativePageNavigationDelegateImpl implements NativePageNavigationDelegate { + private static final String TAG = "PageNavDelegate"; private final Profile mProfile; private final TabModelSelector mTabModelSelector;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java index e4ee217..79ad5024 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/feed/FeedAppLifecycleTest.java
@@ -29,6 +29,7 @@ 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.FlakyTest; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeTabbedActivity; @@ -87,6 +88,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void construction_checks_active_tabbed_activities() { verify(mAppLifecycleListener, times(1)).onEnterForeground(); } @@ -94,6 +96,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void activity_state_changes_increment_state_counters() throws InterruptedException, TimeoutException { assertEquals(0, @@ -119,6 +122,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void ntp_opening_triggers_initialize_only_once() throws InterruptedException { // We open to about:blank initially so we shouldn't have called initialize() yet. verify(mAppLifecycleListener, times(0)).initialize(); @@ -137,6 +141,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void history_deletion_triggers_clear_all() throws InterruptedException { verify(mAppLifecycleListener, times(0)).onClearAll(); mAppLifecycle.onHistoryDeleted(); @@ -149,6 +154,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void cached_data_removal_triggers_clear_all() throws InterruptedException { verify(mAppLifecycleListener, times(0)).onClearAll(); mAppLifecycle.onCachedDataCleared(); @@ -161,6 +167,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void signout_triggers_clear_all() throws InterruptedException { verify(mAppLifecycleListener, times(0)).onClearAll(); mAppLifecycle.onSignedOut(); @@ -173,6 +180,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void signin_triggers_clear_all() throws InterruptedException { verify(mAppLifecycleListener, times(0)).onClearAll(); mAppLifecycle.onSignedIn(); @@ -185,6 +193,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void second_window_does_not_trigger_foreground_or_background() throws InterruptedException, TimeoutException { verify(mAppLifecycleListener, times(1)).onEnterForeground(); @@ -210,6 +219,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void multi_window_does_not_cause_multiple_initialize() throws InterruptedException { mActivityTestRule.loadUrl(UrlConstants.NTP_URL); verify(mAppLifecycleListener, times(1)).initialize(); @@ -224,6 +234,7 @@ @Test @SmallTest @Feature({"InterestFeedContentSuggestions"}) + @FlakyTest(message = "http://crbug.com/891419") public void resume_triggers_scheduler_foregrounded() throws InterruptedException, TimeoutException { // Starting mActivity in setUp() triggers a resume. @@ -260,4 +271,4 @@ waitForStateChangeHelper.waitForCallback(0); } -} \ No newline at end of file +}
diff --git a/chrome/app/printing_strings.grdp b/chrome/app/printing_strings.grdp index 5377be4..d24cda60 100644 --- a/chrome/app/printing_strings.grdp +++ b/chrome/app/printing_strings.grdp
@@ -80,7 +80,7 @@ Scale </message> <message name="IDS_PRINT_PREVIEW_PAGES_PER_SHEET_LABEL" desc="Pages per sheet option label."> - Pages Per Sheet + Pages per sheet </message> <message name="IDS_PRINT_PREVIEW_EXAMPLE_PAGE_RANGE_TEXT" desc="Example page range text."> e.g. 1-5, 8, 11-13
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index c4df5a7..60fc0a63 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3679,7 +3679,7 @@ flag_descriptions::kNewNetErrorPageUIDescription, kOsAndroid, FEATURE_WITH_PARAMS_VALUE_TYPE(features::kNewNetErrorPageUI, kNewNetErrorPageUIVariations, - "NewNetErrorPageUI")}, + "OfflineContentOnDinoPage")}, #endif // defined(OS_ANDROID) #if defined(OS_ANDROID)
diff --git a/chrome/browser/accessibility/accessibility_extension_api.cc b/chrome/browser/accessibility/accessibility_extension_api.cc index cc84c72..7d36c76 100644 --- a/chrome/browser/accessibility/accessibility_extension_api.cc +++ b/chrome/browser/accessibility/accessibility_extension_api.cc
@@ -35,6 +35,7 @@ #include "ui/events/keycodes/keyboard_codes.h" #if defined(OS_CHROMEOS) +#include "ash/public/interfaces/accessibility_controller.mojom.h" #include "ash/public/interfaces/accessibility_focus_ring_controller.mojom.h" #include "ash/public/interfaces/constants.mojom.h" #include "ash/public/interfaces/event_rewriter_controller.mojom.h" @@ -53,6 +54,17 @@ const char kErrorNotSupported[] = "This API is not supported on this platform."; +#if defined(OS_CHROMEOS) +ash::mojom::AccessibilityControllerPtr GetAccessibilityController() { + // Connect to the accessibility mojo interface in ash. + ash::mojom::AccessibilityControllerPtr accessibility_controller; + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->BindInterface(ash::mojom::kServiceName, &accessibility_controller); + return accessibility_controller; +} +#endif + } // namespace ExtensionFunction::ResponseAction @@ -302,4 +314,20 @@ return RespondNow(NoArguments()); } +ExtensionFunction::ResponseAction +AccessibilityPrivateToggleDictationFunction::Run() { + ash::mojom::DictationToggleSource source = + ash::mojom::DictationToggleSource::kChromevox; + if (extension()->id() == extension_misc::kSwitchAccessExtensionId) + source = ash::mojom::DictationToggleSource::kSwitchAccess; + else if (extension()->id() == extension_misc::kChromeVoxExtensionId) + source = ash::mojom::DictationToggleSource::kChromevox; + else + NOTREACHED(); + + GetAccessibilityController()->ToggleDictationFromSource(source); + + return RespondNow(NoArguments()); +} + #endif // defined (OS_CHROMEOS)
diff --git a/chrome/browser/accessibility/accessibility_extension_api.h b/chrome/browser/accessibility/accessibility_extension_api.h index 6726455..dba2f3c 100644 --- a/chrome/browser/accessibility/accessibility_extension_api.h +++ b/chrome/browser/accessibility/accessibility_extension_api.h
@@ -97,6 +97,17 @@ DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.onSelectToSpeakStateChanged", ACCESSIBILITY_PRIVATE_ONSELECTTOSPEAKSTATECHANGED) }; + +// API function that is called when a SwitchAccess user toggles Dictation from +// the context menu. +class AccessibilityPrivateToggleDictationFunction + : public UIThreadExtensionFunction { + ~AccessibilityPrivateToggleDictationFunction() override {} + ResponseAction Run() override; + DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.toggleDictation", + ACCESSIBILITY_PRIVATE_TOGGLEDICTATION) +}; + #endif // defined (OS_CHROMEOS) #endif // CHROME_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EXTENSION_API_H_
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index d077e77..f01dbb2 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -248,7 +248,7 @@ base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kContentSuggestionsScrollToLoad{ - "ContentSuggestionsScrollToLoad", base::FEATURE_ENABLED_BY_DEFAULT}; + "ContentSuggestionsScrollToLoad", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kContentSuggestionsThumbnailDominantColor{ "ContentSuggestionsThumbnailDominantColor",
diff --git a/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc b/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc index 0a0783d..fd62e89 100644 --- a/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc +++ b/chrome/browser/android/explore_sites/explore_sites_bridge_experimental.cc
@@ -14,7 +14,7 @@ #include "chrome/browser/android/explore_sites/catalog.h" #include "chrome/browser/android/explore_sites/catalog.pb.h" #include "chrome/browser/android/explore_sites/ntp_json_fetcher.h" -#include "chrome/browser/android/explore_sites/url_util.h" +#include "chrome/browser/android/explore_sites/url_util_experimental.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_android.h" #include "chrome/browser/search/suggestions/image_decoder_impl.h" @@ -113,7 +113,8 @@ ScopedJavaLocalRef<jstring> JNI_ExploreSitesBridgeExperimental_GetCatalogUrl( JNIEnv* env, const JavaParamRef<jclass>& jcaller) { - return base::android::ConvertUTF8ToJavaString(env, GetCatalogURL().spec()); + return base::android::ConvertUTF8ToJavaString( + env, GetCatalogPrototypeURL().spec()); } // static
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc index 4f99d03..186168a 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -17,6 +17,8 @@ #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" +#include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/test/test_timeouts.h" @@ -79,6 +81,7 @@ #include "components/password_manager/core/browser/password_manager_test_utils.h" #include "components/password_manager/core/browser/password_store_consumer.h" #include "components/prefs/testing_pref_service.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browsing_data_filter_builder.h" #include "content/public/browser/browsing_data_remover.h" #include "content/public/test/browsing_data_remover_test_util.h" @@ -86,11 +89,12 @@ #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "net/cookies/canonical_cookie.h" -#include "net/cookies/cookie_store.h" #include "net/http/http_transaction_factory.h" #include "net/net_buildflags.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_test_util.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/favicon_size.h" @@ -255,70 +259,59 @@ // Returns true, if the given cookie exists in the cookie store. bool ContainsCookie() { - scoped_refptr<content::MessageLoopRunner> message_loop_runner = - new content::MessageLoopRunner; - quit_closure_ = message_loop_runner->QuitClosure(); - get_cookie_success_ = false; - cookie_store_->GetCookieListWithOptionsAsync( + bool result = false; + base::RunLoop run_loop; + cookie_manager_->GetCookieList( kOrigin1, net::CookieOptions(), - base::BindOnce(&RemoveCookieTester::GetCookieListCallback, - base::Unretained(this))); - message_loop_runner->Run(); - return get_cookie_success_; + base::BindLambdaForTesting([&](const net::CookieList& cookie_list) { + std::string cookie_line = + net::CanonicalCookie::BuildCookieLine(cookie_list); + if (cookie_line == "A=1") { + result = true; + } else { + EXPECT_EQ("", cookie_line); + result = false; + } + run_loop.Quit(); + })); + run_loop.Run(); + return result; } void AddCookie() { - scoped_refptr<content::MessageLoopRunner> message_loop_runner = - new content::MessageLoopRunner; - quit_closure_ = message_loop_runner->QuitClosure(); - cookie_store_->SetCookieWithOptionsAsync( - kOrigin1, "A=1", net::CookieOptions(), - base::BindOnce(&RemoveCookieTester::SetCookieCallback, - base::Unretained(this))); - message_loop_runner->Run(); + base::RunLoop run_loop; + auto cookie = net::CanonicalCookie::Create( + kOrigin1, "A=1", base::Time::Now(), net::CookieOptions()); + cookie_manager_->SetCanonicalCookie( + *cookie, /*secure_source=*/false, /*modify_http_only=*/false, + base::BindLambdaForTesting([&](bool result) { + EXPECT_TRUE(result); + run_loop.Quit(); + })); + run_loop.Run(); } protected: - void SetCookieStore(net::CookieStore* cookie_store) { - cookie_store_ = cookie_store; + void SetCookieManager(network::mojom::CookieManagerPtr cookie_manager) { + cookie_manager_ = std::move(cookie_manager); } private: - void GetCookieListCallback(const net::CookieList& cookie_list) { - std::string cookie_line = - net::CanonicalCookie::BuildCookieLine(cookie_list); - if (cookie_line == "A=1") { - get_cookie_success_ = true; - } else { - EXPECT_EQ("", cookie_line); - get_cookie_success_ = false; - } - quit_closure_.Run(); - } - - void SetCookieCallback(bool result) { - ASSERT_TRUE(result); - quit_closure_.Run(); - } - - bool get_cookie_success_ = false; - base::Closure quit_closure_; - - // CookieStore must out live |this|. - net::CookieStore* cookie_store_ = nullptr; + network::mojom::CookieManagerPtr cookie_manager_; DISALLOW_COPY_AND_ASSIGN(RemoveCookieTester); }; -void RunClosureAfterCookiesCleared(const base::Closure& task, - uint32_t cookies_deleted) { - task.Run(); -} - class RemoveSafeBrowsingCookieTester : public RemoveCookieTester { public: RemoveSafeBrowsingCookieTester() : browser_process_(TestingBrowserProcess::GetGlobal()) { + system_request_context_getter_ = + base::MakeRefCounted<net::TestURLRequestContextGetter>( + base::CreateSingleThreadTaskRunnerWithTraits( + {content::BrowserThread::IO})); + browser_process_->SetSystemRequestContext( + system_request_context_getter_.get()); scoped_refptr<safe_browsing::SafeBrowsingService> sb_service = safe_browsing::SafeBrowsingService::CreateSafeBrowsingService(); browser_process_->SetSafeBrowsingService(sb_service.get()); @@ -328,13 +321,16 @@ // Make sure the safe browsing cookie store has no cookies. // TODO(mmenke): Is this really needed? base::RunLoop run_loop; - net::URLRequestContext* request_context = - sb_service->url_request_context()->GetURLRequestContext(); - request_context->cookie_store()->DeleteAllAsync( - base::BindOnce(&RunClosureAfterCookiesCleared, run_loop.QuitClosure())); + network::mojom::CookieManagerPtr cookie_manager; + sb_service->GetNetworkContext()->GetCookieManager( + mojo::MakeRequest(&cookie_manager)); + cookie_manager->DeleteCookies( + network::mojom::CookieDeletionFilter::New(), + base::BindLambdaForTesting( + [&](uint32_t num_deleted) { run_loop.Quit(); })); run_loop.Run(); - SetCookieStore(request_context->cookie_store()); + SetCookieManager(std::move(cookie_manager)); } virtual ~RemoveSafeBrowsingCookieTester() { @@ -345,6 +341,7 @@ private: TestingBrowserProcess* browser_process_; + scoped_refptr<net::URLRequestContextGetter> system_request_context_getter_; DISALLOW_COPY_AND_ASSIGN(RemoveSafeBrowsingCookieTester); };
diff --git a/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc b/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc index 064dd960..dc184dff 100644 --- a/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc +++ b/chrome/browser/browsing_data/counters/site_data_counting_helper_unittest.cc
@@ -4,39 +4,31 @@ #include <algorithm> #include <memory> -#include <set> #include <string> +#include <vector> #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/run_loop.h" #include "base/task/post_task.h" +#include "base/test/bind_test_util.h" #include "chrome/browser/browsing_data/counters/site_data_counting_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" #include "content/public/test/test_browser_thread_bundle.h" -#include "net/cookies/cookie_store.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" #include "testing/gtest/include/gtest/gtest.h" -using content::BrowserThread; - class SiteDataCountingHelperTest : public testing::Test { public: const int64_t kTimeoutMs = 10; void SetUp() override { profile_.reset(new TestingProfile()); - run_loop_.reset(new base::RunLoop()); - tasks_ = 0; - cookie_callback_ = base::Bind(&SiteDataCountingHelperTest::CookieCallback, - base::Unretained(this)); } void TearDown() override { @@ -44,43 +36,35 @@ base::RunLoop().RunUntilIdle(); } - void CookieCallback(int count) { - // Negative values represent an unexpected error. - DCHECK(count >= 0); - last_count_ = count; - - if (run_loop_) - run_loop_->Quit(); - } - - void DoneOnIOThread(bool success) { - DCHECK(success); - if (--tasks_ > 0) - return; - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&SiteDataCountingHelperTest::DoneCallback, - base::Unretained(this))); - } - - void DoneCallback() { run_loop_->Quit(); } - - void WaitForTasksOnIOThread() { - run_loop_->Run(); - run_loop_.reset(new base::RunLoop()); - } - void CreateCookies(base::Time creation_time, const std::vector<std::string>& urls) { content::StoragePartition* partition = content::BrowserContext::GetDefaultStoragePartition(profile()); - net::URLRequestContextGetter* rq_context = - partition->GetURLRequestContext(); - base::PostTaskWithTraits( - FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&SiteDataCountingHelperTest::CreateCookiesOnIOThread, - base::Unretained(this), base::WrapRefCounted(rq_context), - creation_time, urls)); + network::mojom::CookieManager* cookie_manager = + partition->GetCookieManagerForBrowserProcess(); + + base::RunLoop run_loop; + int tasks = urls.size(); + + int i = 0; + for (const std::string& url_string : urls) { + GURL url(url_string); + // Cookies need a unique creation time. + base::Time time = creation_time + base::TimeDelta::FromMilliseconds(i++); + std::unique_ptr<net::CanonicalCookie> cookie = + net::CanonicalCookie::CreateSanitizedCookie( + url, "name", "A=1", url.host(), url.path(), time, base::Time(), + time, url.SchemeIsCryptographic(), false, + net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT); + cookie_manager->SetCanonicalCookie( + *cookie, url.SchemeIsCryptographic(), true /*modify_http_only*/, + base::BindLambdaForTesting([&](bool result) { + if (--tasks == 0) + run_loop.Quit(); + })); + } + + run_loop.Run(); } void CreateLocalStorage( @@ -100,60 +84,31 @@ } } - void CreateCookiesOnIOThread( - const scoped_refptr<net::URLRequestContextGetter>& rq_context, - base::Time creation_time, - std::vector<std::string> urls) { - net::CookieStore* cookie_store = - rq_context->GetURLRequestContext()->cookie_store(); - - tasks_ = urls.size(); - int i = 0; - for (const std::string& url_string : urls) { - GURL url(url_string); - // Cookies need a unique creation time. - base::Time time = creation_time + base::TimeDelta::FromMilliseconds(i++); - - cookie_store->SetCanonicalCookieAsync( - net::CanonicalCookie::CreateSanitizedCookie( - url, "name", "A=1", url.host(), url.path(), time, base::Time(), - time, url.SchemeIsCryptographic(), false, - net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT), - url.SchemeIsCryptographic(), true /*modify_http_only*/, - base::BindOnce(&SiteDataCountingHelperTest::DoneOnIOThread, - base::Unretained(this))); - } - } - - void CountEntries(base::Time begin_time) { - last_count_ = -1; - auto* helper = - new SiteDataCountingHelper(profile(), begin_time, cookie_callback_); + int CountEntries(base::Time begin_time) { + base::RunLoop run_loop; + int result = -1; + auto* helper = new SiteDataCountingHelper( + profile(), begin_time, base::BindLambdaForTesting([&](int count) { + // Negative values represent an unexpected error. + DCHECK_GE(count, 0); + result = count; + run_loop.Quit(); + })); helper->CountAndDestroySelfWhenFinished(); - } + run_loop.Run(); - int64_t GetResult() { - DCHECK_GE(last_count_, 0); - return last_count_; + return result; } Profile* profile() { return profile_.get(); } private: - base::Callback<void(int)> cookie_callback_; - std::unique_ptr<base::RunLoop> run_loop_; content::TestBrowserThreadBundle thread_bundle_; std::unique_ptr<TestingProfile> profile_; - - int tasks_; - int64_t last_count_; }; TEST_F(SiteDataCountingHelperTest, CheckEmptyResult) { - CountEntries(base::Time()); - WaitForTasksOnIOThread(); - - DCHECK_EQ(0, GetResult()); + EXPECT_EQ(0, CountEntries(base::Time())); } TEST_F(SiteDataCountingHelperTest, CountCookies) { @@ -162,26 +117,12 @@ base::Time yesterday = now - base::TimeDelta::FromDays(1); CreateCookies(last_hour, {"https://example.com"}); - WaitForTasksOnIOThread(); - CreateCookies(yesterday, {"https://google.com", "https://bing.com"}); - WaitForTasksOnIOThread(); - CountEntries(base::Time()); - WaitForTasksOnIOThread(); - DCHECK_EQ(3, GetResult()); - - CountEntries(yesterday); - WaitForTasksOnIOThread(); - DCHECK_EQ(3, GetResult()); - - CountEntries(last_hour); - WaitForTasksOnIOThread(); - DCHECK_EQ(1, GetResult()); - - CountEntries(now); - WaitForTasksOnIOThread(); - DCHECK_EQ(0, GetResult()); + EXPECT_EQ(3, CountEntries(base::Time())); + EXPECT_EQ(3, CountEntries(yesterday)); + EXPECT_EQ(1, CountEntries(last_hour)); + EXPECT_EQ(0, CountEntries(now)); } TEST_F(SiteDataCountingHelperTest, LocalStorage) { @@ -190,9 +131,7 @@ {FILE_PATH_LITERAL("https_example.com_443.localstorage"), FILE_PATH_LITERAL("https_bing.com_443.localstorage")}); - CountEntries(base::Time()); - WaitForTasksOnIOThread(); - DCHECK_EQ(2, GetResult()); + EXPECT_EQ(2, CountEntries(base::Time())); } TEST_F(SiteDataCountingHelperTest, CookiesAndLocalStorage) { @@ -201,11 +140,8 @@ CreateLocalStorage(now, {FILE_PATH_LITERAL("https_example.com_443.localstorage"), FILE_PATH_LITERAL("https_bing.com_443.localstorage")}); - WaitForTasksOnIOThread(); - CountEntries(base::Time()); - WaitForTasksOnIOThread(); - DCHECK_EQ(3, GetResult()); + EXPECT_EQ(3, CountEntries(base::Time())); } TEST_F(SiteDataCountingHelperTest, SameHostDifferentScheme) { @@ -214,8 +150,6 @@ CreateLocalStorage(now, {FILE_PATH_LITERAL("https_google.com_443.localstorage"), FILE_PATH_LITERAL("http_google.com_80.localstorage")}); - WaitForTasksOnIOThread(); - CountEntries(base::Time()); - WaitForTasksOnIOThread(); - DCHECK_EQ(1, GetResult()); + + EXPECT_EQ(1, CountEntries(base::Time())); }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index c5f05ba8..c79f73f8 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1494,6 +1494,20 @@ #endif } +network::mojom::URLLoaderFactoryPtrInfo +ChromeContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + return ChromeContentBrowserClientExtensionsPart:: + CreateURLLoaderFactoryForNetworkRequests(process, network_context, + request_initiator); +#else + return network::mojom::URLLoaderFactoryPtrInfo(); +#endif +} + // These are treated as WebUI schemes but do not get WebUI bindings. Also, // view-source is allowed for these schemes. void ChromeContentBrowserClient::GetAdditionalWebUISchemes(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index c229f00..30a59ea 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -131,6 +131,11 @@ const url::Origin& initiator_origin, int render_process_id, content::ResourceType resource_type) override; + network::mojom::URLLoaderFactoryPtrInfo + CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) override; void GetAdditionalWebUISchemes( std::vector<std::string>* additional_schemes) override; void GetAdditionalViewSourceSchemes(
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json index 2e5854f..3734fee 100644 --- a/chrome/browser/chrome_content_browser_manifest_overlay.json +++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -90,13 +90,13 @@ "chrome.mojom.OpenSearchDocumentDescriptionHandler", "chrome.mojom.PrerenderCanceler", "chromeos.ime.mojom.InputEngineManager", + "chromeos.media_perception.mojom.MediaPerception", "contextual_search.mojom.ContextualSearchJsApiService", "dom_distiller.mojom.DistillabilityService", "dom_distiller.mojom.DistillerJavaScriptService", "extensions.KeepAlive", "extensions.mime_handler.BeforeUnloadControl", "extensions.mime_handler.MimeHandlerService", - "extensions.mojom.InlineInstall", "media_router.mojom.MediaRouter", "page_load_metrics.mojom.PageLoadMetrics", "safe_browsing.mojom.PhishingDetectorClient",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index e74c82e..f1ca707 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -83,6 +83,7 @@ "//chromeos:cryptohome_proto", "//chromeos:cryptohome_signkey_proto", "//chromeos:login_manager_proto", + "//chromeos:media_perception_proto", "//chromeos:metrics_event_proto", "//chromeos:oobe_config_proto", "//chromeos/assistant:buildflags",
diff --git a/chrome/browser/chromeos/arc/pip/OWNERS b/chrome/browser/chromeos/arc/pip/OWNERS new file mode 100644 index 0000000..ae6d94c --- /dev/null +++ b/chrome/browser/chromeos/arc/pip/OWNERS
@@ -0,0 +1 @@ +edcourtney@chromium.org
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/crostini/crostini_share_path.cc index 27cd4565..8a8d3f8 100644 --- a/chrome/browser/chromeos/crostini/crostini_share_path.cc +++ b/chrome/browser/chromeos/crostini/crostini_share_path.cc
@@ -76,7 +76,7 @@ request.mutable_shared_path()->set_writable(true); request.set_storage_location( vm_tools::seneschal::SharePathRequest::DOWNLOADS); - request.set_owner_id(CryptohomeIdForProfile(profile)); + request.set_owner_id(crostini::CryptohomeIdForProfile(profile)); chromeos::DBusThreadManager::Get()->GetSeneschalClient()->SharePath( request, base::BindOnce(&OnSeneschalSharePathResponse, std::move(path), std::move(callback)));
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc index 31a2887..010e4af 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.cc +++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -82,7 +82,7 @@ browser->window()->Close(); if (result == crostini::ConciergeClientResult::OFFLINE_WHEN_UPGRADE_REQUIRED) { - ShowCrostiniUpgradeView(profile, CrostiniUISurface::kAppList); + ShowCrostiniUpgradeView(profile, crostini::CrostiniUISurface::kAppList); } return; } @@ -210,6 +210,8 @@ } // namespace +namespace crostini { + void SetCrostiniUIAllowedForTesting(bool enabled) { g_crostini_ui_allowed_for_testing = enabled; } @@ -407,3 +409,5 @@ // If device policy is not set, allow Crostini. return true; } + +} // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_util.h b/chrome/browser/chromeos/crostini/crostini_util.h index 4c0bc89..947b36d 100644 --- a/chrome/browser/chromeos/crostini/crostini_util.h +++ b/chrome/browser/chromeos/crostini/crostini_util.h
@@ -22,6 +22,8 @@ class Profile; +namespace crostini { + // Enables/disables overriding IsCrostiniUIAllowedForProfile's normal // behaviour and returning true instead. void SetCrostiniUIAllowedForTesting(bool enabled); @@ -124,4 +126,6 @@ // policy. bool IsUnaffiliatedCrostiniAllowedByPolicy(); +} // namespace crostini + #endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UTIL_H_
diff --git a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc index 1918195..60f6eadf 100644 --- a/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc +++ b/chrome/browser/chromeos/dbus/chrome_features_service_provider.cc
@@ -82,7 +82,7 @@ std::unique_ptr<dbus::Response> response = dbus::Response::FromMethodCall(method_call); dbus::MessageWriter writer(response.get()); - writer.AppendBool(IsCrostiniAllowedForProfile(profile)); + writer.AppendBool(crostini::IsCrostiniAllowedForProfile(profile)); response_sender.Run(std::move(response)); }
diff --git a/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc b/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc index 57fbf16..c00547b3 100644 --- a/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc +++ b/chrome/browser/chromeos/dbus/vm_applications_service_provider.cc
@@ -78,7 +78,7 @@ } Profile* profile = ProfileManager::GetPrimaryUserProfile(); - if (IsCrostiniEnabled(profile)) { + if (crostini::IsCrostiniEnabled(profile)) { crostini::CrostiniRegistryService* registry_service = crostini::CrostiniRegistryServiceFactory::GetForProfile(profile); registry_service->UpdateApplicationList(request); @@ -104,8 +104,8 @@ } Profile* profile = ProfileManager::GetPrimaryUserProfile(); - if (IsCrostiniEnabled(profile) && - request.owner_id() == CryptohomeIdForProfile(profile)) { + if (crostini::IsCrostiniEnabled(profile) && + request.owner_id() == crostini::CryptohomeIdForProfile(profile)) { crostini::CrostiniManager::GetForProfile(profile)->LaunchContainerTerminal( request.vm_name(), request.container_name(), std::vector<std::string>(request.params().begin(), @@ -131,7 +131,7 @@ } Profile* profile = ProfileManager::GetPrimaryUserProfile(); - if (IsCrostiniEnabled(profile)) { + if (crostini::IsCrostiniEnabled(profile)) { crostini::CrostiniMimeTypesService* mime_types_service = crostini::CrostiniMimeTypesServiceFactory::GetForProfile(profile); mime_types_service->UpdateMimeTypes(request);
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc index 48ee56e..61bb906 100644 --- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc +++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -695,7 +695,7 @@ DVLOG(1) << "AutotestPrivateSetCrostiniEnabledFunction " << params->enabled; Profile* profile = Profile::FromBrowserContext(browser_context()); - if (!IsCrostiniUIAllowedForProfile(profile)) + if (!crostini::IsCrostiniUIAllowedForProfile(profile)) return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError)); // Set the preference to indicate Crostini is enabled/disabled. @@ -721,7 +721,7 @@ DVLOG(1) << "AutotestPrivateInstallCrostiniFunction"; Profile* profile = Profile::FromBrowserContext(browser_context()); - if (!IsCrostiniUIAllowedForProfile(profile)) + if (!crostini::IsCrostiniUIAllowedForProfile(profile)) return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError)); // Run GUI installer which will install crostini vm / container and @@ -731,7 +731,7 @@ CrostiniInstallerView::Show(profile); CrostiniInstallerView::GetActiveViewForTesting()->Accept(); crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::kCrostiniDefaultVmName, crostini::kCrostiniDefaultContainerName, base::BindOnce( &AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted, this)); @@ -760,7 +760,7 @@ DVLOG(1) << "AutotestPrivateRunCrostiniUninstallerFunction"; Profile* profile = Profile::FromBrowserContext(browser_context()); - if (!IsCrostiniUIAllowedForProfile(profile)) + if (!crostini::IsCrostiniUIAllowedForProfile(profile)) return RespondNow(Error(kCrostiniNotAvailableForCurrentUserError)); // Run GUI uninstaller which will remove crostini vm / container. We then
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc index 7613bfc..1179ce8 100644 --- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc +++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -548,7 +548,7 @@ crostini::CrostiniManager::GetForProfile(browser()->profile()); crostini_manager->set_skip_restart_for_testing(); vm_tools::concierge::VmInfo vm_info; - crostini_manager->AddRunningVmForTesting(kCrostiniDefaultVmName, + crostini_manager->AddRunningVmForTesting(crostini::kCrostiniDefaultVmName, std::move(vm_info)); ExpectCrostiniMount();
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc index cdf02000..cafa74c 100644 --- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc +++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -645,8 +645,9 @@ ExtensionFunction::ResponseAction FileManagerPrivateIsCrostiniEnabledFunction::Run() { - return RespondNow(OneArgument(std::make_unique<base::Value>( - IsCrostiniEnabled(Profile::FromBrowserContext(browser_context()))))); + return RespondNow( + OneArgument(std::make_unique<base::Value>(crostini::IsCrostiniEnabled( + Profile::FromBrowserContext(browser_context()))))); } FileManagerPrivateMountCrostiniFunction:: @@ -660,9 +661,9 @@ // files into Linux files should still work. Profile* profile = Profile::FromBrowserContext(browser_context())->GetOriginalProfile(); - DCHECK(IsCrostiniEnabled(profile)); + DCHECK(crostini::IsCrostiniEnabled(profile)); crostini::CrostiniManager::GetForProfile(profile)->RestartCrostini( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::kCrostiniDefaultVmName, crostini::kCrostiniDefaultContainerName, base::BindOnce(&FileManagerPrivateMountCrostiniFunction::RestartCallback, this)); return true; @@ -693,7 +694,7 @@ file_system_context->CrackURL(GURL(params->url)); crostini::SharePath( - profile, kCrostiniDefaultVmName, cracked.path(), + profile, crostini::kCrostiniDefaultVmName, cracked.path(), base::BindOnce(&FileManagerPrivateInternalSharePathWithCrostiniFunction:: SharePathCallback, this)); @@ -762,7 +763,8 @@ profile, file_system_context->CrackURL(GURL(params->url))); crostini::CrostiniPackageInstallerService::GetForProfile(profile) ->InstallLinuxPackage( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, url, + crostini::kCrostiniDefaultVmName, + crostini::kCrostiniDefaultContainerName, url, base::BindOnce( &FileManagerPrivateInternalInstallLinuxPackageFunction:: OnInstallLinuxPackage,
diff --git a/chrome/browser/chromeos/file_manager/crostini_file_tasks.cc b/chrome/browser/chromeos/file_manager/crostini_file_tasks.cc index afd4c8f..1bf1ffc 100644 --- a/chrome/browser/chromeos/file_manager/crostini_file_tasks.cc +++ b/chrome/browser/chromeos/file_manager/crostini_file_tasks.cc
@@ -89,7 +89,7 @@ const std::vector<extensions::EntryInfo>& entries, std::vector<FullTaskDescriptor>* result_list, base::OnceClosure completion_closure) { - if (!IsCrostiniUIAllowedForProfile(profile)) { + if (!crostini::IsCrostiniUIAllowedForProfile(profile)) { std::move(completion_closure).Run(); return; } @@ -138,7 +138,7 @@ ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactors().back(); - LoadIcons( + crostini::LoadIcons( profile, result_app_ids, kIconSizeInDip, scale_factor, kIconLoadTimeout, base::BindOnce(OnAppIconsLoaded, profile, result_app_ids, scale_factor, result_list, std::move(completion_closure))); @@ -149,7 +149,7 @@ const TaskDescriptor& task, const std::vector<storage::FileSystemURL>& file_system_urls, FileTaskFinishedCallback done) { - DCHECK(IsCrostiniUIAllowedForProfile(profile)); + DCHECK(crostini::IsCrostiniUIAllowedForProfile(profile)); std::vector<std::string> files; for (const storage::FileSystemURL& file_system_url : file_system_urls) { @@ -157,7 +157,8 @@ profile, file_system_url)); } - LaunchCrostiniApp(profile, task.app_id, display::kInvalidDisplayId, files); + crostini::LaunchCrostiniApp(profile, task.app_id, display::kInvalidDisplayId, + files); } } // namespace file_tasks
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc index 995f237..a9a58d81 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1432,7 +1432,7 @@ crostini::CrostiniManager* crostini_manager = crostini::CrostiniManager::GetForProfile(profile()->GetOriginalProfile()); vm_tools::concierge::VmInfo vm_info; - crostini_manager->AddRunningVmForTesting(kCrostiniDefaultVmName, + crostini_manager->AddRunningVmForTesting(crostini::kCrostiniDefaultVmName, std::move(vm_info)); return crostini_volume_->mount_path(); }
diff --git a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc index c7fc5f4..0fd02e3 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks_unittest.cc
@@ -1128,8 +1128,8 @@ // Setup the custom MIME type mapping. vm_tools::apps::MimeTypes mime_types_list; - mime_types_list.set_vm_name(kCrostiniDefaultVmName); - mime_types_list.set_container_name(kCrostiniDefaultContainerName); + mime_types_list.set_vm_name(crostini::kCrostiniDefaultVmName); + mime_types_list.set_container_name(crostini::kCrostiniDefaultContainerName); (*mime_types_list.mutable_mime_type_mappings())["foo"] = "foo/x-bar"; crostini::CrostiniMimeTypesServiceFactory::GetForProfile(&test_profile_)
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc index 1243149..ba253bdf 100644 --- a/chrome/browser/chromeos/file_manager/path_util.cc +++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -151,8 +151,9 @@ std::string GetCrostiniMountPointName(Profile* profile) { // crostini_<hash>_termina_penguin return base::JoinString( - {"crostini", CryptohomeIdForProfile(profile), kCrostiniDefaultVmName, - kCrostiniDefaultContainerName}, + {"crostini", crostini::CryptohomeIdForProfile(profile), + crostini::kCrostiniDefaultVmName, + crostini::kCrostiniDefaultContainerName}, "_"); } @@ -198,10 +199,11 @@ base::FilePath folder; if (id == mount_point_name_crostini) { folder = base::FilePath(mount_point_name_crostini); - result = ContainerHomeDirectoryForProfile(profile); + result = crostini::ContainerHomeDirectoryForProfile(profile); } else if (id == mount_point_name_downloads) { folder = base::FilePath(mount_point_name_downloads); - result = ContainerChromeOSBaseDirectory().Append(kDownloadsFolderName); + result = + crostini::ContainerChromeOSBaseDirectory().Append(kDownloadsFolderName); } else { NOTREACHED(); }
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc index ba5413a..11d4b1a 100644 --- a/chrome/browser/chromeos/file_manager/volume_manager.cc +++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -557,7 +557,8 @@ // Listen for crostini container shutdown and remove volume. crostini::CrostiniManager::GetForProfile(profile_) ->AddShutdownContainerCallback( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::kCrostiniDefaultVmName, + crostini::kCrostiniDefaultContainerName, base::BindOnce(&VolumeManager::RemoveSshfsCrostiniVolume, weak_ptr_factory_.GetWeakPtr(), sshfs_mount_path)); }
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.cc b/chrome/browser/chromeos/login/lock/views_screen_locker.cc index 6d7f143..846ab21 100644 --- a/chrome/browser/chromeos/login/lock/views_screen_locker.cc +++ b/chrome/browser/chromeos/login/lock/views_screen_locker.cc
@@ -8,6 +8,7 @@ #include <string> #include <utility> +#include "ash/public/cpp/ash_features.h" #include "ash/public/interfaces/login_user_info.mojom.h" #include "base/bind.h" #include "base/i18n/time_formatting.h" @@ -28,6 +29,7 @@ #include "chrome/browser/ui/ash/wallpaper_controller_client.h" #include "chrome/common/pref_names.h" #include "chromeos/components/proximity_auth/screenlock_bridge.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/login/auth/authpolicy_login_helper.h" #include "components/user_manager/known_user.h" #include "components/user_manager/user_manager.h" @@ -38,6 +40,7 @@ namespace { constexpr char kLockDisplay[] = "lock"; +constexpr char kExternalBinaryAuth[] = "external_binary_auth"; ash::mojom::FingerprintUnlockState ConvertFromFingerprintState( ScreenLocker::FingerprintState state) { @@ -57,11 +60,40 @@ } } +void OnSetState(base::Optional<mri::State> maybe_state) { + // TODO/FIXME: add handling for starting the graph process. + NOTIMPLEMENTED(); +} + +// Starts the graph specified by |configuration| if the current graph +// is SUSPENDED or if the current configuration is different. +void StartGraphIfNeeded(chromeos::MediaAnalyticsClient* client, + const std::string& configuration, + base::Optional<mri::State> maybe_state) { + if (!maybe_state) + return; + + if (maybe_state->status() == mri::State::SUSPENDED) { + mri::State new_state; + new_state.set_status(mri::State::RUNNING); + new_state.set_configuration(configuration); + client->SetState(new_state, base::BindOnce(&OnSetState)); + } else if (maybe_state->configuration() != configuration) { + mri::State suspend_state; + suspend_state.set_status(mri::State::SUSPENDED); + suspend_state.set_configuration(configuration); + client->SetState(suspend_state, base::BindOnce(&StartGraphIfNeeded, client, + configuration)); + } +} + } // namespace ViewsScreenLocker::ViewsScreenLocker(ScreenLocker* screen_locker) : screen_locker_(screen_locker), version_info_updater_(std::make_unique<MojoVersionInfoDispatcher>()), + media_analytics_client_( + chromeos::DBusThreadManager::Get()->GetMediaAnalyticsClient()), weak_factory_(this) { LoginScreenClient::Get()->SetDelegate(this); user_board_view_mojo_ = std::make_unique<UserBoardViewMojo>(); @@ -74,6 +106,9 @@ kDeviceLoginScreenInputMethods, base::Bind(&ViewsScreenLocker::OnAllowedInputMethodsChanged, base::Unretained(this))); + + if (base::FeatureList::IsEnabled(ash::features::kUnlockWithExternalBinary)) + scoped_observer_.Add(media_analytics_client_); } ViewsScreenLocker::~ViewsScreenLocker() { @@ -198,6 +233,8 @@ void ViewsScreenLocker::HandleAuthenticateUserWithExternalBinary( const AccountId& account_id, AuthenticateUserWithExternalBinaryCallback callback) { + media_analytics_client_->GetState(base::BindOnce( + &StartGraphIfNeeded, media_analytics_client_, kExternalBinaryAuth)); // TODO/FIXME: implement the actual external binary auth check. bool auth_success = false; NOTIMPLEMENTED(); @@ -299,6 +336,13 @@ reverse); } +void ViewsScreenLocker::OnDetectionSignal( + const mri::MediaPerception& media_perception) { + // TODO/FIXME: respond to positive/negative signal from external binary + // auth service. + NOTIMPLEMENTED(); +} + void ViewsScreenLocker::UpdatePinKeyboardState(const AccountId& account_id) { quick_unlock::PinBackend::GetInstance()->CanAuthenticate( account_id, base::BindOnce(&ViewsScreenLocker::OnPinCanAuthenticate,
diff --git a/chrome/browser/chromeos/login/lock/views_screen_locker.h b/chrome/browser/chromeos/login/lock/views_screen_locker.h index 7688ac3..30aa64b 100644 --- a/chrome/browser/chromeos/login/lock/views_screen_locker.h +++ b/chrome/browser/chromeos/login/lock/views_screen_locker.h
@@ -6,10 +6,13 @@ #define CHROME_BROWSER_CHROMEOS_LOGIN_LOCK_VIEWS_SCREEN_LOCKER_H_ #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" #include "chrome/browser/chromeos/lock_screen_apps/focus_cycler_delegate.h" #include "chrome/browser/chromeos/login/lock/screen_locker.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/ui/ash/login_screen_client.h" +#include "chromeos/dbus/media_analytics_client.h" +#include "chromeos/dbus/media_perception/media_perception.pb.h" #include "chromeos/dbus/power_manager_client.h" namespace chromeos { @@ -25,7 +28,8 @@ class ViewsScreenLocker : public LoginScreenClient::Delegate, public ScreenLocker::Delegate, public PowerManagerClient::Observer, - public lock_screen_apps::FocusCyclerDelegate { + public lock_screen_apps::FocusCyclerDelegate, + public chromeos::MediaAnalyticsClient::Observer { public: explicit ViewsScreenLocker(ScreenLocker* screen_locker); ~ViewsScreenLocker() override; @@ -78,6 +82,9 @@ void UnregisterLockScreenAppFocusHandler() override; void HandleLockScreenAppFocusOut(bool reverse) override; + // chromeos::MediaAnalyticsClient::Observer + void OnDetectionSignal(const mri::MediaPerception& media_perception) override; + private: void UpdatePinKeyboardState(const AccountId& account_id); void OnAllowedInputMethodsChanged(); @@ -109,6 +116,11 @@ // Updates UI when version info is changed. std::unique_ptr<MojoVersionInfoDispatcher> version_info_updater_; + chromeos::MediaAnalyticsClient* media_analytics_client_; + + ScopedObserver<chromeos::MediaAnalyticsClient, ViewsScreenLocker> + scoped_observer_{this}; + base::WeakPtrFactory<ViewsScreenLocker> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ViewsScreenLocker);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index cdd80d4..60ae146 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1859,6 +1859,13 @@ return; } + // Skip key update when using PIN. The keys should wrap password instead of + // PIN. + if (user_context.IsUsingPin()) { + NotifyEasyUnlockKeyOpsFinished(); + return; + } + const base::ListValue* device_list = nullptr; EasyUnlockService* easy_unlock_service = EasyUnlockService::GetForUser(*user); if (easy_unlock_service) {
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc index cf9089c..9bb1f9f 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc +++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -590,13 +590,15 @@ else finalize_animation_type_ = ANIMATION_NONE; - // Observe the user switch animation and defer the deletion of itself only - // after the animation is finished. - MultiUserWindowManager* window_manager = - MultiUserWindowManager::GetInstance(); - // MultiUserWindowManager instance might be nullptr in a unit test. - if (window_manager) - window_manager->AddObserver(this); + if (finalize_animation_type_ == ANIMATION_ADD_USER) { + // Observe the user switch animation and defer the deletion of itself only + // after the animation is finished. + MultiUserWindowManager* window_manager = + MultiUserWindowManager::GetInstance(); + // MultiUserWindowManager instance might be nullptr in a unit test. + if (window_manager) + window_manager->AddObserver(this); + } VLOG(1) << "Login WebUI >> user adding"; if (!login_window_)
diff --git a/chrome/browser/chromeos/smb_client/smb_url.cc b/chrome/browser/chromeos/smb_client/smb_url.cc index f95a92b..46818ab94 100644 --- a/chrome/browser/chromeos/smb_client/smb_url.cc +++ b/chrome/browser/chromeos/smb_client/smb_url.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/chromeos/smb_client/smb_url.h" +#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "chrome/browser/chromeos/smb_client/smb_constants.h" #include "url/url_canon_stdstring.h" @@ -13,6 +14,7 @@ namespace { +const char kSingleBackslash[] = "\\"; const char kDoubleBackslash[] = "\\\\"; // Returns true if |url| starts with "smb://" or "\\". @@ -59,6 +61,8 @@ if (ShouldProcessUrl(raw_url)) { // Add "smb://" if |url| starts with "\\" and canonicalize the URL. CanonicalizeSmbUrl(AddSmbSchemeIfMissing(raw_url)); + // Create the Windows UNC for the url. + CreateWindowsUnc(raw_url); } } @@ -90,6 +94,12 @@ return !url_.empty() && host_.is_valid(); } +std::string SmbUrl::GetWindowsUNCString() const { + DCHECK(IsValid()); + + return windows_unc_; +} + void SmbUrl::CanonicalizeSmbUrl(const std::string& url) { DCHECK(!IsValid()); DCHECK(ShouldProcessUrl(url)); @@ -135,6 +145,21 @@ DCHECK_EQ(url_.substr(scheme.begin, scheme.len), kSmbScheme); } +void SmbUrl::CreateWindowsUnc(const std::string& url) { + url::Parsed parsed; + if (!ParseAndValidateUrl(url, &parsed)) { + return; + } + + const std::string host = url.substr(parsed.host.begin, parsed.host.len); + std::string path = url.substr(parsed.path.begin, parsed.path.len); + + // Turn any forward slashes into escaped backslashes. + base::ReplaceChars(path, "/", kSingleBackslash, &path); + + windows_unc_ = base::StrCat({kDoubleBackslash, host, path}); +} + void SmbUrl::Reset() { host_.reset(); url_.clear();
diff --git a/chrome/browser/chromeos/smb_client/smb_url.h b/chrome/browser/chromeos/smb_client/smb_url.h index a07e424..3e23a45 100644 --- a/chrome/browser/chromeos/smb_client/smb_url.h +++ b/chrome/browser/chromeos/smb_client/smb_url.h
@@ -38,16 +38,26 @@ // should be called after the constructor. bool IsValid() const; + // Returns |url_| in the format \\server\share. + std::string GetWindowsUNCString() const; + private: // Canonicalize |url| and saves the output as url_ and host_ if successful. void CanonicalizeSmbUrl(const std::string& url); + // Parse |url| into a Windows UNC |windows_unc_|. + void CreateWindowsUnc(const std::string& url); + // Resets url_ and parsed_. void Reset(); // String form of the canonical url. std::string url_; + // String form of the Windows Universal Naming Convention of the url. + // MS-DTYP section 2.2.57 + std::string windows_unc_; + // Holds the identified host of the URL. This does not store the host itself. url::Component host_;
diff --git a/chrome/browser/chromeos/smb_client/smb_url_unittest.cc b/chrome/browser/chromeos/smb_client/smb_url_unittest.cc index 7bbca31..92d9399 100644 --- a/chrome/browser/chromeos/smb_client/smb_url_unittest.cc +++ b/chrome/browser/chromeos/smb_client/smb_url_unittest.cc
@@ -31,6 +31,13 @@ EXPECT_EQ(expected_host, smb_url.GetHost()); } + void ExpectValidWindowsUNC(const std::string& url, + const std::string& expected_unc) { + SmbUrl smb_url(url); + EXPECT_TRUE(smb_url.IsValid()); + EXPECT_EQ(expected_unc, smb_url.GetWindowsUNCString()); + } + private: DISALLOW_COPY_AND_ASSIGN(SmbUrlTest); }; @@ -103,5 +110,16 @@ EXPECT_EQ(expected_host, smb_url.GetHost()); } +TEST_F(SmbUrlTest, GetWindowsURL) { + ExpectValidWindowsUNC("smb://server/share", "\\\\server\\share"); + ExpectValidWindowsUNC("smb://server/share/long/folder", + "\\\\server\\share\\long\\folder"); + ExpectValidWindowsUNC("smb://server/share/folder.with.dots", + "\\\\server\\share\\folder.with.dots"); + ExpectValidWindowsUNC("smb://server\\share/mixed\\slashes", + "\\\\server\\share\\mixed\\slashes"); + ExpectValidWindowsUNC("\\\\server/share", "\\\\server\\share"); +} + } // namespace smb_client } // namespace chromeos
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc index 643c19c..ca0e39f8 100644 --- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc +++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -273,8 +273,8 @@ bool equals = remote_locations.size() == remote_locations_.size(); if (equals) { - RemoteLocations::iterator it1 = remote_locations.begin(); - RemoteLocations::iterator it2 = remote_locations_.begin(); + auto it1 = remote_locations.begin(); + auto it2 = remote_locations_.begin(); while (it1 != remote_locations.end()) { DCHECK(it2 != remote_locations_.end()); if (!(*it1).Equals(*it2))
diff --git a/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc b/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc index 1288d9c..8c46ddf 100644 --- a/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc +++ b/chrome/browser/devtools/device/adb/adb_client_socket_browsertest.cc
@@ -16,8 +16,7 @@ static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> FindBrowserByDisplayName(DevToolsAndroidBridge::RemoteBrowsers browsers, const std::string& name) { - for (DevToolsAndroidBridge::RemoteBrowsers::iterator it = browsers.begin(); - it != browsers.end(); ++it) + for (auto it = browsers.begin(); it != browsers.end(); ++it) if ((*it)->display_name() == name) return *it; return NULL;
diff --git a/chrome/browser/devtools/device/android_device_info_query.cc b/chrome/browser/devtools/device/android_device_info_query.cc index 8dbbb4d..cae3802 100644 --- a/chrome/browser/devtools/device/android_device_info_query.cc +++ b/chrome/browser/devtools/device/android_device_info_query.cc
@@ -246,8 +246,7 @@ if (!unix_user.empty() && unix_user[0] == 'u') { size_t pos = unix_user.find('_'); if (pos != std::string::npos) { - StringMap::const_iterator it = - id_to_username.find(unix_user.substr(1, pos - 1)); + auto it = id_to_username.find(unix_user.substr(1, pos - 1)); if (it != id_to_username.end()) return it->second; } @@ -310,7 +309,7 @@ std::string socket = pair.first; std::string pid = pair.second; std::string package; - StringMap::iterator pit = pid_to_package.find(pid); + auto pit = pid_to_package.find(pid); if (pit != pid_to_package.end()) package = pit->second; @@ -320,7 +319,7 @@ browser_info.display_name = AndroidDeviceManager::GetBrowserName(socket, package); - StringMap::iterator uit = pid_to_user.find(pid); + auto uit = pid_to_user.find(pid); if (uit != pid_to_user.end()) browser_info.user = GetUserName(uit->second, id_to_username);
diff --git a/chrome/browser/devtools/device/android_device_manager.cc b/chrome/browser/devtools/device/android_device_manager.cc index 8399552..74558dce 100644 --- a/chrome/browser/devtools/device/android_device_manager.cc +++ b/chrome/browser/devtools/device/android_device_manager.cc
@@ -339,8 +339,7 @@ DevicesRequest* request = new DevicesRequest(callback); // Avoid destruction while sending requests request->AddRef(); - for (DeviceProviders::const_iterator it = providers.begin(); - it != providers.end(); ++it) { + for (auto it = providers.begin(); it != providers.end(); ++it) { device_task_runner->PostTask( FROM_HERE, base::BindOnce(&DeviceProvider::QueryDevices, *it, base::Bind(&DevicesRequest::ProcessSerials, @@ -365,8 +364,7 @@ void ProcessSerials(scoped_refptr<DeviceProvider> provider, const Serials& serials) { - for (Serials::const_iterator it = serials.begin(); it != serials.end(); - ++it) { + for (auto it = serials.begin(); it != serials.end(); ++it) { descriptors_->resize(descriptors_->size() + 1); descriptors_->back().provider = provider; descriptors_->back().serial = *it; @@ -546,8 +544,7 @@ void AndroidDeviceManager::SetDeviceProviders( const DeviceProviders& providers) { - for (DeviceProviders::iterator it = providers_.begin(); - it != providers_.end(); ++it) { + for (auto it = providers_.begin(); it != providers_.end(); ++it) { (*it)->AddRef(); DeviceProvider* raw_ptr = it->get(); *it = nullptr; @@ -580,7 +577,7 @@ for (DeviceDescriptors::const_iterator it = descriptors->begin(); it != descriptors->end(); ++it) { - DeviceWeakMap::iterator found = devices_.find(it->serial); + auto found = devices_.find(it->serial); scoped_refptr<Device> device; if (found == devices_.end() || !found->second || found->second->provider_.get() != it->provider.get()) {
diff --git a/chrome/browser/devtools/device/devtools_android_bridge.cc b/chrome/browser/devtools/device/devtools_android_bridge.cc index db3f446..9a2f7a3 100644 --- a/chrome/browser/devtools/device/devtools_android_bridge.cc +++ b/chrome/browser/devtools/device/devtools_android_bridge.cc
@@ -106,7 +106,7 @@ scoped_refptr<content::DevToolsAgentHost> DevToolsAndroidBridge::GetBrowserAgentHost( scoped_refptr<RemoteBrowser> browser) { - DeviceMap::iterator it = device_map_.find(browser->serial()); + auto it = device_map_.find(browser->serial()); if (it == device_map_.end()) return nullptr; @@ -123,7 +123,7 @@ callback.Run(net::ERR_FAILED, std::string()); return; } - DeviceMap::iterator it = device_map_.find(serial); + auto it = device_map_.find(serial); if (it == device_map_.end()) { callback.Run(net::ERR_FAILED, std::string()); return; @@ -186,8 +186,8 @@ void DevToolsAndroidBridge::RemoveDeviceListListener( DeviceListListener* listener) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DeviceListListeners::iterator it = std::find( - device_list_listeners_.begin(), device_list_listeners_.end(), listener); + auto it = std::find(device_list_listeners_.begin(), + device_list_listeners_.end(), listener); DCHECK(it != device_list_listeners_.end()); device_list_listeners_.erase(it); if (!NeedsDeviceListPolling()) @@ -204,8 +204,8 @@ void DevToolsAndroidBridge::RemoveDeviceCountListener( DeviceCountListener* listener) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DeviceCountListeners::iterator it = std::find( - device_count_listeners_.begin(), device_count_listeners_.end(), listener); + auto it = std::find(device_count_listeners_.begin(), + device_count_listeners_.end(), listener); DCHECK(it != device_count_listeners_.end()); device_count_listeners_.erase(it); if (device_count_listeners_.empty()) @@ -222,10 +222,8 @@ void DevToolsAndroidBridge::RemovePortForwardingListener( PortForwardingListener* listener) { - PortForwardingListeners::iterator it = std::find( - port_forwarding_listeners_.begin(), - port_forwarding_listeners_.end(), - listener); + auto it = std::find(port_forwarding_listeners_.begin(), + port_forwarding_listeners_.end(), listener); DCHECK(it != port_forwarding_listeners_.end()); port_forwarding_listeners_.erase(it); if (!NeedsDeviceListPolling()) @@ -269,14 +267,14 @@ } DeviceListListeners copy(device_list_listeners_); - for (DeviceListListeners::iterator it = copy.begin(); it != copy.end(); ++it) + for (auto it = copy.begin(); it != copy.end(); ++it) (*it)->DeviceListChanged(remote_devices); ForwardingStatus status = port_forwarding_controller_->DeviceListChanged(complete_devices); PortForwardingListeners forwarding_listeners(port_forwarding_listeners_); - for (PortForwardingListeners::iterator it = forwarding_listeners.begin(); - it != forwarding_listeners.end(); ++it) { + for (auto it = forwarding_listeners.begin(); it != forwarding_listeners.end(); + ++it) { (*it)->PortStatusChanged(status); } } @@ -305,7 +303,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DeviceCountListeners copy(device_count_listeners_); - for (DeviceCountListeners::iterator it = copy.begin(); it != copy.end(); ++it) + for (auto it = copy.begin(); it != copy.end(); ++it) (*it)->DeviceCountChanged(count); if (device_count_listeners_.empty())
diff --git a/chrome/browser/devtools/device/devtools_device_discovery.cc b/chrome/browser/devtools/device/devtools_device_discovery.cc index da58bbf..fbc6efe 100644 --- a/chrome/browser/devtools/device/devtools_device_discovery.cc +++ b/chrome/browser/devtools/device/devtools_device_discovery.cc
@@ -428,7 +428,7 @@ scoped_refptr<RemoteDevice> remote_device = new RemoteDevice(device->serial(), device_info); complete_devices_.push_back(std::make_pair(device, remote_device)); - for (RemoteBrowsers::iterator it = remote_device->browsers().begin(); + for (auto it = remote_device->browsers().begin(); it != remote_device->browsers().end(); ++it) { device->SendJsonRequest( (*it)->socket(), @@ -563,10 +563,8 @@ model_(device_info.model), connected_(device_info.connected), screen_size_(device_info.screen_size) { - for (std::vector<AndroidDeviceManager::BrowserInfo>::const_iterator it = - device_info.browser_info.begin(); - it != device_info.browser_info.end(); - ++it) { + for (auto it = device_info.browser_info.begin(); + it != device_info.browser_info.end(); ++it) { browsers_.push_back(new RemoteBrowser(serial, *it)); } }
diff --git a/chrome/browser/devtools/device/port_forwarding_controller.cc b/chrome/browser/devtools/device/port_forwarding_controller.cc index 866bb33..29770838 100644 --- a/chrome/browser/devtools/device/port_forwarding_controller.cc +++ b/chrome/browser/devtools/device/port_forwarding_controller.cc
@@ -389,11 +389,10 @@ const ForwardingMap& old_map, const ForwardingMap& new_map) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - for (ForwardingMap::const_iterator new_it(new_map.begin()); - new_it != new_map.end(); ++new_it) { + for (auto new_it(new_map.begin()); new_it != new_map.end(); ++new_it) { int port = new_it->first; const std::string& location = new_it->second; - ForwardingMap::const_iterator old_it = old_map.find(port); + auto old_it = old_map.find(port); if (old_it != old_map.end() && old_it->second == location) continue; // The port points to the same location in both configs, skip. @@ -421,7 +420,7 @@ port_status_[port] = kStatusConnecting; #endif // BUILDFLAG(DEBUG_DEVTOOLS) } else { - PortStatusMap::iterator it = port_status_.find(port); + auto it = port_status_.find(port); if (it != port_status_.end() && it->second == kStatusError) { // The bind command failed on this port, do not attempt unbind. port_status_.erase(it); @@ -446,7 +445,7 @@ if (!ParseResponse(message, &id, &error_code)) return false; - CommandCallbackMap::iterator it = pending_responses_.find(id); + auto it = pending_responses_.find(id); if (it == pending_responses_.end()) return false; @@ -462,7 +461,7 @@ void PortForwardingController::Connection::ProcessUnbindResponse( int port, PortStatus status) { - PortStatusMap::iterator it = port_status_.find(port); + auto it = port_status_.find(port); if (it == port_status_.end()) return; if (status == kStatusError) @@ -507,7 +506,7 @@ !params->GetString(kConnectionIdParam, &connection_id)) return; - std::map<int, std::string>::iterator it = forwarding_map_.find(port); + auto it = forwarding_map_.find(port); if (it == forwarding_map_.end()) return; @@ -549,7 +548,7 @@ pair.second); if (!remote_device->is_connected()) continue; - Registry::iterator rit = registry_.find(remote_device->serial()); + auto rit = registry_.find(remote_device->serial()); if (rit == registry_.end()) { if (remote_device->browsers().size() > 0) { new Connection(profile_, ®istry_, device, @@ -592,6 +591,6 @@ } void PortForwardingController::UpdateConnections() { - for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it) + for (auto it = registry_.begin(); it != registry_.end(); ++it) it->second->UpdateForwardingMap(forwarding_map_); }
diff --git a/chrome/browser/devtools/device/usb/android_usb_device.cc b/chrome/browser/devtools/device/usb/android_usb_device.cc index dda4ad5..5df1dffe 100644 --- a/chrome/browser/devtools/device/usb/android_usb_device.cc +++ b/chrome/browser/devtools/device/usb/android_usb_device.cc
@@ -621,9 +621,9 @@ case AdbMessage::kCommandWRTE: case AdbMessage::kCommandCLSE: { - AndroidUsbSockets::iterator it = sockets_.find(message->arg1); - if (it != sockets_.end()) - it->second->HandleIncoming(std::move(message)); + auto it = sockets_.find(message->arg1); + if (it != sockets_.end()) + it->second->HandleIncoming(std::move(message)); } break; default: @@ -652,8 +652,7 @@ void AndroidUsbDevice::Terminate() { DCHECK(task_runner_->BelongsToCurrentThread()); - std::vector<AndroidUsbDevice*>::iterator it = - std::find(g_devices.Get().begin(), g_devices.Get().end(), this); + auto it = std::find(g_devices.Get().begin(), g_devices.Get().end(), this); if (it != g_devices.Get().end()) g_devices.Get().erase(it); @@ -667,8 +666,7 @@ // Iterate over copy. AndroidUsbSockets sockets(sockets_); - for (AndroidUsbSockets::iterator it = sockets.begin(); - it != sockets.end(); ++it) { + for (auto it = sockets.begin(); it != sockets.end(); ++it) { it->second->Terminated(true); } DCHECK(sockets_.empty());
diff --git a/chrome/browser/devtools/device/usb/usb_device_provider.cc b/chrome/browser/devtools/device/usb/usb_device_provider.cc index 52faa6f7..cc9db68 100644 --- a/chrome/browser/devtools/device/usb/usb_device_provider.cc +++ b/chrome/browser/devtools/device/usb/usb_device_provider.cc
@@ -101,7 +101,7 @@ void UsbDeviceProvider::QueryDeviceInfo(const std::string& serial, const DeviceInfoCallback& callback) { - UsbDeviceMap::iterator it = device_map_.find(serial); + auto it = device_map_.find(serial); if (it == device_map_.end() || !it->second->is_connected()) { AndroidDeviceManager::DeviceInfo offline_info; callback.Run(offline_info); @@ -114,7 +114,7 @@ void UsbDeviceProvider::OpenSocket(const std::string& serial, const std::string& name, const SocketCallback& callback) { - UsbDeviceMap::iterator it = device_map_.find(serial); + auto it = device_map_.find(serial); if (it == device_map_.end()) { callback.Run(net::ERR_CONNECTION_FAILED, base::WrapUnique<net::StreamSocket>(NULL)); @@ -144,8 +144,7 @@ const AndroidUsbDevices& devices) { std::vector<std::string> result; device_map_.clear(); - for (AndroidUsbDevices::const_iterator it = devices.begin(); - it != devices.end(); ++it) { + for (auto it = devices.begin(); it != devices.end(); ++it) { result.push_back((*it)->serial()); device_map_[(*it)->serial()] = *it; (*it)->InitOnCallerThread();
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc index 61a0bf3..46fe046 100644 --- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc +++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.cc
@@ -119,7 +119,7 @@ bool Dispatch(const DispatchCallback& callback, const std::string& method, const base::ListValue* params) override { - HandlerMap::iterator it = handlers_.find(method); + auto it = handlers_.find(method); return it != handlers_.end() && it->second.Run(callback, *params); }
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc index 9c791d9eb..f411c2c 100644 --- a/chrome/browser/devtools/devtools_file_helper.cc +++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -229,7 +229,7 @@ bool save_as, const SaveCallback& saveCallback, const CancelCallback& cancelCallback) { - PathsMap::iterator it = saved_files_.find(url); + auto it = saved_files_.find(url); if (it != saved_files_.end() && !save_as) { SaveAsFileSelected(url, content, saveCallback, it->second); return; @@ -276,7 +276,7 @@ void DevToolsFileHelper::Append(const std::string& url, const std::string& content, const AppendCallback& callback) { - PathsMap::iterator it = saved_files_.find(url); + auto it = saved_files_.find(url); if (it == saved_files_.end()) return; callback.Run();
diff --git a/chrome/browser/devtools/devtools_file_system_indexer.cc b/chrome/browser/devtools/devtools_file_system_indexer.cc index 8d458a12..ca1a46a 100644 --- a/chrome/browser/devtools/devtools_file_system_indexer.cc +++ b/chrome/browser/devtools/devtools_file_system_indexer.cc
@@ -184,7 +184,7 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); EnsureInitialized(); FileId file_id = GetFileId(file_path); - vector<Trigram>::const_iterator it = index.begin(); + auto it = index.begin(); for (; it != index.end(); ++it) { Trigram trigram = *it; index_[trigram].push_back(file_id);
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc index 0e28dbe1..830d664 100644 --- a/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -566,9 +566,7 @@ extensions::ProcessManager* manager = extensions::ProcessManager::Get(browser()->profile()); extensions::ProcessManager::FrameSet all_frames = manager->GetAllFrames(); - for (extensions::ProcessManager::FrameSet::const_iterator iter = - all_frames.begin(); - iter != all_frames.end();) { + for (auto iter = all_frames.begin(); iter != all_frames.end();) { if (!content::WebContents::FromRenderFrameHost(*iter)->IsLoading()) ++iter; else
diff --git a/chrome/browser/devtools/devtools_targets_ui.cc b/chrome/browser/devtools/devtools_targets_ui.cc index 4342c6b..da914c2 100644 --- a/chrome/browser/devtools/devtools_targets_ui.cc +++ b/chrome/browser/devtools/devtools_targets_ui.cc
@@ -210,7 +210,7 @@ void AdbTargetsUIHandler::Open(const std::string& browser_id, const std::string& url) { - RemoteBrowsers::iterator it = remote_browsers_.find(browser_id); + auto it = remote_browsers_.find(browser_id); if (it != remote_browsers_.end() && android_bridge_) android_bridge_->OpenRemotePage(it->second, url); } @@ -218,7 +218,7 @@ scoped_refptr<DevToolsAgentHost> AdbTargetsUIHandler::GetBrowserAgentHost( const std::string& browser_id) { - RemoteBrowsers::iterator it = remote_browsers_.find(browser_id); + auto it = remote_browsers_.find(browser_id); if (it == remote_browsers_.end() || !android_bridge_) return nullptr; @@ -233,8 +233,7 @@ return; base::ListValue device_list; - for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit = - devices.begin(); dit != devices.end(); ++dit) { + for (auto dit = devices.begin(); dit != devices.end(); ++dit) { DevToolsAndroidBridge::RemoteDevice* device = dit->get(); std::unique_ptr<base::DictionaryValue> device_data( new base::DictionaryValue()); @@ -248,8 +247,7 @@ auto browser_list = std::make_unique<base::ListValue>(); DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers(); - for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit = - browsers.begin(); bit != browsers.end(); ++bit) { + for (auto bit = browsers.begin(); bit != browsers.end(); ++bit) { DevToolsAndroidBridge::RemoteBrowser* browser = bit->get(); std::unique_ptr<base::DictionaryValue> browser_data( new base::DictionaryValue()); @@ -324,7 +322,7 @@ scoped_refptr<DevToolsAgentHost> DevToolsTargetsUIHandler::GetTarget( const std::string& target_id) { - TargetMap::iterator it = targets_.find(target_id); + auto it = targets_.find(target_id); if (it != targets_.end()) return it->second; return NULL; @@ -383,12 +381,10 @@ void PortForwardingStatusSerializer::PortStatusChanged( const ForwardingStatus& status) { base::DictionaryValue result; - for (ForwardingStatus::const_iterator sit = status.begin(); - sit != status.end(); ++sit) { + for (auto sit = status.begin(); sit != status.end(); ++sit) { auto port_status_dict = std::make_unique<base::DictionaryValue>(); const PortStatusMap& port_status_map = sit->second; - for (PortStatusMap::const_iterator it = port_status_map.begin(); - it != port_status_map.end(); ++it) { + for (auto it = port_status_map.begin(); it != port_status_map.end(); ++it) { port_status_dict->SetInteger(base::IntToString(it->first), it->second); }
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc index 12474d2..425953b42 100644 --- a/chrome/browser/devtools/devtools_ui_bindings.cc +++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -505,8 +505,7 @@ return NULL; DevToolsUIBindingsList* instances = g_devtools_ui_bindings_instances.Pointer(); - for (DevToolsUIBindingsList::iterator it(instances->begin()); - it != instances->end(); ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->web_contents() == web_contents) return *it; } @@ -550,8 +549,7 @@ // Remove self from global list. DevToolsUIBindingsList* instances = g_devtools_ui_bindings_instances.Pointer(); - DevToolsUIBindingsList::iterator it( - std::find(instances->begin(), instances->end(), this)); + auto it(std::find(instances->begin(), instances->end(), this)); DCHECK(it != instances->end()); instances->erase(it); } @@ -825,7 +823,7 @@ void DevToolsUIBindings::StopIndexing(int index_request_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - IndexingJobsMap::iterator it = indexing_jobs_.find(index_request_id); + auto it = indexing_jobs_.find(index_request_id); if (it == indexing_jobs_.end()) return; it->second->Stop(); @@ -1229,8 +1227,7 @@ const std::vector<std::string>& file_paths) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::ListValue file_paths_value; - for (std::vector<std::string>::const_iterator it(file_paths.begin()); - it != file_paths.end(); ++it) { + for (auto it(file_paths.begin()); it != file_paths.end(); ++it) { file_paths_value.AppendString(*it); } base::Value request_id_value(request_id);
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc index fd99d17..1e6f9486 100644 --- a/chrome/browser/devtools/devtools_window.cc +++ b/chrome/browser/devtools/devtools_window.cc
@@ -269,7 +269,7 @@ base::ListValue* shortcut_list; if (!parsed_message || !parsed_message->GetAsList(&shortcut_list)) return; - base::ListValue::iterator it = shortcut_list->begin(); + auto it = shortcut_list->begin(); for (; it != shortcut_list->end(); ++it) { base::DictionaryValue* dictionary; if (!it->GetAsDictionary(&dictionary)) @@ -421,8 +421,7 @@ owned_toolbox_web_contents_.reset(); DevToolsWindows* instances = g_devtools_window_instances.Pointer(); - DevToolsWindows::iterator it( - std::find(instances->begin(), instances->end(), this)); + auto it(std::find(instances->begin(), instances->end(), this)); DCHECK(it != instances->end()); instances->erase(it); @@ -484,8 +483,7 @@ if (!inspected_web_contents || !g_devtools_window_instances.IsCreated()) return NULL; DevToolsWindows* instances = g_devtools_window_instances.Pointer(); - for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); - ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->GetInspectedWebContents() == inspected_web_contents) return *it; } @@ -497,8 +495,7 @@ if (!web_contents || !g_devtools_window_instances.IsCreated()) return false; DevToolsWindows* instances = g_devtools_window_instances.Pointer(); - for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); - ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->main_web_contents_ == web_contents || (*it)->toolbox_web_contents_ == web_contents) return true; @@ -1094,8 +1091,7 @@ if (!agent_host || !g_devtools_window_instances.IsCreated()) return NULL; DevToolsWindows* instances = g_devtools_window_instances.Pointer(); - for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); - ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->bindings_->IsAttachedTo(agent_host)) return *it; } @@ -1108,8 +1104,7 @@ if (!web_contents || !g_devtools_window_instances.IsCreated()) return NULL; DevToolsWindows* instances = g_devtools_window_instances.Pointer(); - for (DevToolsWindows::iterator it(instances->begin()); it != instances->end(); - ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->main_web_contents_ == web_contents) return *it; }
diff --git a/chrome/browser/devtools/devtools_window_testing.cc b/chrome/browser/devtools/devtools_window_testing.cc index c0c99ff..c4cbcab 100644 --- a/chrome/browser/devtools/devtools_window_testing.cc +++ b/chrome/browser/devtools/devtools_window_testing.cc
@@ -36,8 +36,7 @@ DevToolsWindowTesting::~DevToolsWindowTesting() { DevToolsWindowTestings* instances = g_devtools_window_testing_instances.Pointer(); - DevToolsWindowTestings::iterator it( - std::find(instances->begin(), instances->end(), this)); + auto it(std::find(instances->begin(), instances->end(), this)); DCHECK(it != instances->end()); instances->erase(it); if (!close_callback_.is_null()) { @@ -64,9 +63,7 @@ return NULL; DevToolsWindowTestings* instances = g_devtools_window_testing_instances.Pointer(); - for (DevToolsWindowTestings::iterator it(instances->begin()); - it != instances->end(); - ++it) { + for (auto it(instances->begin()); it != instances->end(); ++it) { if ((*it)->devtools_window_ == window) return *it; }
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index abdd74a..12c729e3 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -562,8 +562,6 @@ "extension_message_bubble_controller.h", "extension_migrator.cc", "extension_migrator.h", - "extension_reenabler.cc", - "extension_reenabler.h", "extension_service.cc", "extension_service.h", "extension_special_storage_policy.cc", @@ -728,10 +726,6 @@ "webstore_data_fetcher.h", "webstore_data_fetcher_delegate.cc", "webstore_data_fetcher_delegate.h", - "webstore_inline_installer.cc", - "webstore_inline_installer.h", - "webstore_inline_installer_factory.cc", - "webstore_inline_installer_factory.h", "webstore_install_helper.cc", "webstore_install_helper.h", "webstore_install_with_prompt.cc", @@ -742,8 +736,6 @@ "webstore_reinstaller.h", "webstore_standalone_installer.cc", "webstore_standalone_installer.h", - "webstore_startup_installer.cc", - "webstore_startup_installer.h", "window_controller.cc", "window_controller.h", "window_controller_list.cc", @@ -792,7 +784,6 @@ "//chrome/browser/web_applications/components", "//chrome/browser/web_applications/extensions", "//chrome/common", - "//chrome/common/extensions:mojo_bindings", "//chrome/common/extensions/api:extensions_features", "//chrome/common/safe_browsing:proto", "//chrome/services/removable_storage_writer/public/mojom", @@ -977,6 +968,9 @@ "//ash/public/cpp", "//chromeos/components/proximity_auth", "//chromeos/services/ime/public/mojom", + "//chromeos/services/machine_learning/public/cpp", + "//chromeos/services/machine_learning/public/mojom", + "//chromeos/services/media_perception/public/mojom", "//components/arc", "//components/chrome_apps", "//components/constrained_window",
diff --git a/chrome/browser/extensions/api/inline_install_private/inline_install_private_api.cc b/chrome/browser/extensions/api/inline_install_private/inline_install_private_api.cc index 66322d8..f49f337 100644 --- a/chrome/browser/extensions/api/inline_install_private/inline_install_private_api.cc +++ b/chrome/browser/extensions/api/inline_install_private/inline_install_private_api.cc
@@ -30,10 +30,6 @@ friend class base::RefCountedThreadSafe<Installer>; ~Installer() override; - // Needed so that we send the right referrer value in requests to the - // webstore. - const GURL& GetRequestorURL() const override { return requestor_url_; } - std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() const override; @@ -58,7 +54,7 @@ const { std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt( new ExtensionInstallPrompt::Prompt( - ExtensionInstallPrompt::INLINE_INSTALL_PROMPT)); + ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT)); prompt->SetWebstoreData(localized_user_count(), show_user_count(), average_rating(),
diff --git a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc index b5542d7..920bd4d8 100644 --- a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc +++ b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/component_updater/cros_component_installer_chromeos.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/delegate_to_browser_gpu_service_accelerator_factory.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/common/service_manager_connection.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/service_manager/public/cpp/connector.h" @@ -89,4 +90,19 @@ (*provider)->InjectGpuDependencies(std::move(accelerator_factory)); } +void MediaPerceptionAPIDelegateChromeOS::SetMediaPerceptionRequestHandler( + MediaPerceptionRequestHandler handler) { + handler_ = std::move(handler); +} + +void MediaPerceptionAPIDelegateChromeOS::ForwardMediaPerceptionRequest( + chromeos::media_perception::mojom::MediaPerceptionRequest request, + content::RenderFrameHost* render_frame_host) { + if (!handler_) { + DLOG(ERROR) << "Got request but the handler is not set."; + return; + } + handler_.Run(std::move(request)); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h index 5f75d1e..1d765110 100644 --- a/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h +++ b/chrome/browser/extensions/api/media_perception_private/media_perception_api_delegate_chromeos.h
@@ -22,8 +22,15 @@ LoadCrOSComponentCallback load_callback) override; void BindDeviceFactoryProviderToVideoCaptureService( video_capture::mojom::DeviceFactoryProviderPtr* provider) override; + void SetMediaPerceptionRequestHandler( + MediaPerceptionRequestHandler handler) override; + void ForwardMediaPerceptionRequest( + chromeos::media_perception::mojom::MediaPerceptionRequest request, + content::RenderFrameHost* render_frame_host) override; private: + MediaPerceptionRequestHandler handler_; + DISALLOW_COPY_AND_ASSIGN(MediaPerceptionAPIDelegateChromeOS); };
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc index 94aece1..b225140 100644 --- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc +++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -1594,23 +1594,27 @@ // Create a profile that will be destroyed later. base::ScopedAllowBlockingForTesting allow_blocking; ProfileManager* profile_manager = g_browser_process->profile_manager(); - Profile* tmp_profile = Profile::CreateProfile( + Profile* temp_profile = Profile::CreateProfile( profile_manager->user_data_dir().AppendASCII("profile"), nullptr, Profile::CreateMode::CREATE_MODE_SYNCHRONOUS); // Create a WebRequestAPI instance that we can control the lifetime of. - auto api = std::make_unique<WebRequestAPI>(tmp_profile); - // Make sure we are proxying for |tmp_profile|. + auto api = std::make_unique<WebRequestAPI>(temp_profile); + // Make sure we are proxying for |temp_profile|. api->ForceProxyForTesting(); - content::BrowserContext::GetDefaultStoragePartition(tmp_profile) + content::BrowserContext::GetDefaultStoragePartition(temp_profile) ->FlushNetworkInterfaceForTesting(); network::mojom::URLLoaderFactoryPtr factory; auto request = mojo::MakeRequest(&factory); - EXPECT_TRUE(api->MaybeProxyURLLoaderFactory(nullptr, false, &request)); + auto temp_web_contents = + WebContents::Create(WebContents::CreateParams(temp_profile)); + EXPECT_TRUE(api->MaybeProxyURLLoaderFactory(temp_web_contents->GetMainFrame(), + false, &request)); + temp_web_contents.reset(); auto params = network::mojom::URLLoaderFactoryParams::New(); params->process_id = 0; - content::BrowserContext::GetDefaultStoragePartition(tmp_profile) + content::BrowserContext::GetDefaultStoragePartition(temp_profile) ->GetNetworkContext() ->CreateURLLoaderFactory(std::move(request), std::move(params)); @@ -1627,7 +1631,7 @@ // WebRequestAPI. This will cause the connection error that will reach the // proxy before the ProxySet shutdown code runs on the IO thread. api->Shutdown(); - ProfileDestroyer::DestroyProfileWhenAppropriate(tmp_profile); + ProfileDestroyer::DestroyProfileWhenAppropriate(temp_profile); client.Unbind(); api.reset(); }
diff --git a/chrome/browser/extensions/api/webstore_widget_private/app_installer.cc b/chrome/browser/extensions/api/webstore_widget_private/app_installer.cc index bc08c60..025bfdb 100644 --- a/chrome/browser/extensions/api/webstore_widget_private/app_installer.cc +++ b/chrome/browser/extensions/api/webstore_widget_private/app_installer.cc
@@ -53,10 +53,6 @@ return web_contents_ != NULL; } -const GURL& AppInstaller::GetRequestorURL() const { - return GURL::EmptyGURL(); -} - std::unique_ptr<ExtensionInstallPrompt::Prompt> AppInstaller::CreateInstallPrompt() const { if (silent_installation_) @@ -64,7 +60,7 @@ std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt( new ExtensionInstallPrompt::Prompt( - ExtensionInstallPrompt::INLINE_INSTALL_PROMPT)); + ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT)); prompt->SetWebstoreData(localized_user_count(), show_user_count(), average_rating(), rating_count()); @@ -83,40 +79,6 @@ return web_contents_; } -bool AppInstaller::CheckInlineInstallPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - DCHECK(error != NULL); - DCHECK(error->empty()); - - // We expect to be able to inline install the app. - bool inline_install_not_supported = false; - if (webstore_data.HasKey(kInlineInstallNotSupportedKey) && - !webstore_data.GetBoolean(kInlineInstallNotSupportedKey, - &inline_install_not_supported)) { - *error = extensions::webstore_install::kInvalidWebstoreResponseError; - return false; - } - - DCHECK(!inline_install_not_supported) - << "App does not support inline installation"; - - if (inline_install_not_supported) { - *error = extensions::webstore_install::kInvalidWebstoreResponseError; - return false; - } - - return true; -} - -bool AppInstaller::CheckRequestorPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - DCHECK(error != NULL); - DCHECK(error->empty()); - return true; -} - void AppInstaller::OnWebContentsDestroyed(content::WebContents* web_contents) { callback_.Run(false, kWebContentsDestroyedError, extensions::webstore_install::OTHER_ERROR);
diff --git a/chrome/browser/extensions/api/webstore_widget_private/app_installer.h b/chrome/browser/extensions/api/webstore_widget_private/app_installer.h index b142bf7..7f379fe 100644 --- a/chrome/browser/extensions/api/webstore_widget_private/app_installer.h +++ b/chrome/browser/extensions/api/webstore_widget_private/app_installer.h
@@ -37,16 +37,11 @@ // WebstoreStandaloneInstaller implementation. bool CheckRequestorAlive() const override; - const GURL& GetRequestorURL() const override; bool ShouldShowPostInstallUI() const override; bool ShouldShowAppInstalledBubble() const override; content::WebContents* GetWebContents() const override; std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() const override; - bool CheckInlineInstallPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; - bool CheckRequestorPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; private: class WebContentsObserver;
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc index a9380ce..147d3e6 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -59,6 +59,7 @@ #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" #include "extensions/browser/info_map.h" #include "extensions/browser/io_thread_extension_message_filter.h" +#include "extensions/browser/url_loader_factory_manager.h" #include "extensions/browser/url_request_util.h" #include "extensions/browser/view_type_utils.h" #include "extensions/common/constants.h" @@ -851,6 +852,17 @@ } // static +network::mojom::URLLoaderFactoryPtrInfo +ChromeContentBrowserClientExtensionsPart:: + CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) { + return URLLoaderFactoryManager::CreateFactory(process, network_context, + request_initiator); +} + +// static void ChromeContentBrowserClientExtensionsPart::RecordShouldAllowOpenURLFailure( ShouldAllowOpenURLFailureReason reason, const GURL& site_url) {
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h index 2f0a8c84..8668fc39 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.h
@@ -11,10 +11,13 @@ #include "base/macros.h" #include "chrome/browser/chrome_content_browser_client_parts.h" #include "content/public/common/resource_type.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "ui/base/page_transition_types.h" namespace content { struct Referrer; +class RenderProcessHost; class ResourceContext; class VpnServiceProxy; } @@ -89,6 +92,12 @@ int render_process_id, content::ResourceType resource_type); + static network::mojom::URLLoaderFactoryPtrInfo + CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator); + private: FRIEND_TEST_ALL_PREFIXES(ChromeContentBrowserClientExtensionsPartTest, ShouldAllowOpenURLMetricsForEmptySiteURL);
diff --git a/chrome/browser/extensions/chrome_extensions_interface_registration.cc b/chrome/browser/extensions/chrome_extensions_interface_registration.cc index 81dc39fb..be85bab 100644 --- a/chrome/browser/extensions/chrome_extensions_interface_registration.cc +++ b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
@@ -20,7 +20,10 @@ #include "chromeos/chromeos_features.h" #include "chromeos/services/ime/public/mojom/constants.mojom.h" #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" +#include "chromeos/services/media_perception/public/mojom/media_perception.mojom.h" #include "content/public/common/service_manager_connection.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h" #include "services/service_manager/public/cpp/connector.h" #endif @@ -65,6 +68,25 @@ &ForwardRequest<chromeos::ime::mojom::InputEngineManager>, chromeos::ime::mojom::kServiceName)); } + + if (extension->permissions_data()->HasAPIPermission( + APIPermission::kMediaPerceptionPrivate)) { + extensions::ExtensionsAPIClient* client = + extensions::ExtensionsAPIClient::Get(); + extensions::MediaPerceptionAPIDelegate* delegate = nullptr; + if (client) + delegate = client->GetMediaPerceptionAPIDelegate(); + if (delegate) { + // Note that it is safe to use base::Unretained here because |delegate| is + // owned by the |client|, which is instantiated by the + // ChromeExtensionsBrowserClient, which in turn is owned and lives as long + // as the BrowserProcessImpl. + registry->AddInterface( + base::BindRepeating(&extensions::MediaPerceptionAPIDelegate:: + ForwardMediaPerceptionRequest, + base::Unretained(delegate))); + } + } #endif }
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc index 2f7ef04..8694f7c 100644 --- a/chrome/browser/extensions/extension_install_prompt.cc +++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -53,9 +53,9 @@ namespace { bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type) { - return type == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT || - type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT || - type == ExtensionInstallPrompt::REPAIR_PROMPT; + return type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT || + type == ExtensionInstallPrompt::REPAIR_PROMPT || + type == ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT; } // Returns bitmap for the default icon with size equal to the default icon's @@ -179,7 +179,7 @@ int id = -1; switch (type_) { case INSTALL_PROMPT: - case INLINE_INSTALL_PROMPT: + case WEBSTORE_WIDGET_PROMPT: id = IDS_EXTENSION_INSTALL_PROMPT_TITLE; break; case RE_ENABLE_PROMPT: @@ -234,7 +234,7 @@ int id = -1; switch (type_) { case INSTALL_PROMPT: - case INLINE_INSTALL_PROMPT: + case WEBSTORE_WIDGET_PROMPT: if (extension_->is_app()) id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP; else if (extension_->is_theme()) @@ -295,7 +295,7 @@ int id = -1; switch (type_) { case INSTALL_PROMPT: - case INLINE_INSTALL_PROMPT: + case WEBSTORE_WIDGET_PROMPT: case RE_ENABLE_PROMPT: case REMOTE_INSTALL_PROMPT: case REPAIR_PROMPT: @@ -323,7 +323,7 @@ int id = -1; switch (type_) { case INSTALL_PROMPT: - case INLINE_INSTALL_PROMPT: + case WEBSTORE_WIDGET_PROMPT: case EXTERNAL_INSTALL_PROMPT: case REMOTE_INSTALL_PROMPT: case DELEGATED_PERMISSIONS_PROMPT: @@ -361,13 +361,6 @@ return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT; } -bool ExtensionInstallPrompt::Prompt::ShouldUseTabModalDialog() const { - // For inline install, we want the install prompt to be tab modal so that the - // dialog is always clearly associated with the page that made the inline - // install request. - return type_ == INLINE_INSTALL_PROMPT; -} - void ExtensionInstallPrompt::Prompt::AppendRatingStars( StarAppender appender, void* data) const { CHECK(appender);
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h index db454db0..ec13a56b 100644 --- a/chrome/browser/extensions/extension_install_prompt.h +++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -55,7 +55,7 @@ enum PromptType { UNSET_PROMPT_TYPE = -1, INSTALL_PROMPT = 0, - INLINE_INSTALL_PROMPT = 1, + // INLINE_INSTALL_PROMPT_DEPRECATED = 1, // BUNDLE_INSTALL_PROMPT_DEPRECATED = 2, RE_ENABLE_PROMPT = 3, PERMISSIONS_PROMPT = 4, @@ -67,6 +67,7 @@ DELEGATED_PERMISSIONS_PROMPT = 10, // DELEGATED_BUNDLE_PERMISSIONS_PROMPT_DEPRECATED = 11, NUM_PROMPT_TYPES = 12, + WEBSTORE_WIDGET_PROMPT = 13, }; // The last prompt type to display; only used for testing. @@ -111,8 +112,6 @@ bool ShouldShowPermissions() const; - bool ShouldUseTabModalDialog() const; - // Getters for webstore metadata. Only populated when the type is // INLINE_INSTALL_PROMPT, EXTERNAL_INSTALL_PROMPT, or REPAIR_PROMPT.
diff --git a/chrome/browser/extensions/extension_reenabler.cc b/chrome/browser/extensions/extension_reenabler.cc deleted file mode 100644 index 407166a..0000000 --- a/chrome/browser/extensions/extension_reenabler.cc +++ /dev/null
@@ -1,193 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/extension_reenabler.h" - -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/webstore_data_fetcher.h" -#include "chrome/browser/extensions/webstore_inline_installer.h" -#include "chrome/browser/net/system_network_context_manager.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/storage_partition.h" -#include "content/public/browser/web_contents.h" -#include "extensions/browser/extension_prefs.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" -#include "extensions/common/extension.h" - -namespace extensions { - -ExtensionReenabler::~ExtensionReenabler() { - if (!finished_) - Finish(ABORTED); -} - -// static -std::unique_ptr<ExtensionReenabler> ExtensionReenabler::PromptForReenable( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - content::WebContents* web_contents, - const GURL& referrer_url, - const Callback& callback) { -#if DCHECK_IS_ON() - // We should only try to reenable an extension that is, in fact, disabled. - DCHECK(ExtensionRegistry::Get(browser_context)->disabled_extensions(). - Contains(extension->id())); - // Currently, this should only be used for extensions that are disabled due - // to a permissions increase. - int disable_reasons = - ExtensionPrefs::Get(browser_context)->GetDisableReasons(extension->id()); - DCHECK_NE(0, disable_reasons & disable_reason::DISABLE_PERMISSIONS_INCREASE); -#endif // DCHECK_IS_ON() - - return base::WrapUnique(new ExtensionReenabler( - extension, browser_context, referrer_url, callback, web_contents, - ExtensionInstallPrompt::GetDefaultShowDialogCallback())); -} - -// static -std::unique_ptr<ExtensionReenabler> -ExtensionReenabler::PromptForReenableWithCallbackForTest( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - const Callback& callback, - const ExtensionInstallPrompt::ShowDialogCallback& show_dialog_callback) { - return base::WrapUnique(new ExtensionReenabler(extension, browser_context, - GURL(), callback, nullptr, - show_dialog_callback)); -} - -ExtensionReenabler::ExtensionReenabler( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - const GURL& referrer_url, - const Callback& callback, - content::WebContents* web_contents, - const ExtensionInstallPrompt::ShowDialogCallback& show_dialog_callback) - : extension_(extension), - browser_context_(browser_context), - referrer_url_(referrer_url), - callback_(callback), - show_dialog_callback_(show_dialog_callback), - finished_(false), - registry_observer_(this), - weak_factory_(this) { - DCHECK(extension_.get()); - registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); - - install_prompt_.reset(new ExtensionInstallPrompt(web_contents)); - - // If we have a non-empty referrer, then we have to validate that it's a valid - // url for the extension. - if (!referrer_url_.is_empty()) { - webstore_data_fetcher_.reset( - new WebstoreDataFetcher(this, referrer_url_, extension->id())); - webstore_data_fetcher_->Start( - content::BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetURLLoaderFactoryForBrowserProcess() - .get()); - } else { - ExtensionInstallPrompt::PromptType type = - ExtensionInstallPrompt::GetReEnablePromptTypeForExtension( - browser_context, extension.get()); - install_prompt_->ShowDialog( - base::Bind(&ExtensionReenabler::OnInstallPromptDone, - weak_factory_.GetWeakPtr()), - extension.get(), nullptr, - std::make_unique<ExtensionInstallPrompt::Prompt>(type), - show_dialog_callback_); - } -} - -void ExtensionReenabler::OnInstallPromptDone( - ExtensionInstallPrompt::Result install_result) { - ReenableResult result = ABORTED; - switch (install_result) { - case ExtensionInstallPrompt::Result::ACCEPTED: { - // Stop observing - we don't want to see our own enablement. - registry_observer_.RemoveAll(); - - ExtensionService* extension_service = - ExtensionSystem::Get(browser_context_)->extension_service(); - if (extension_service->browser_terminating()) { - result = ABORTED; - } else { - extension_service->GrantPermissionsAndEnableExtension(extension_.get()); - // The re-enable could have failed if the extension is disallowed by - // policy. - bool enabled = ExtensionRegistry::Get(browser_context_) - ->enabled_extensions() - .GetByID(extension_->id()) != nullptr; - result = enabled ? REENABLE_SUCCESS : NOT_ALLOWED; - } - break; - } - case ExtensionInstallPrompt::Result::USER_CANCELED: - result = USER_CANCELED; - break; - case ExtensionInstallPrompt::Result::ABORTED: - result = ABORTED; - break; - } - - Finish(result); -} - -void ExtensionReenabler::OnExtensionLoaded( - content::BrowserContext* browser_context, - const Extension* extension) { - // If the user chose to manually re-enable the extension then, for all - // intents and purposes, this was a success. - if (extension == extension_.get()) - Finish(REENABLE_SUCCESS); -} - -void ExtensionReenabler::OnExtensionUninstalled( - content::BrowserContext* browser_context, - const Extension* extension, - UninstallReason reason) { - if (extension == extension_.get()) - Finish(USER_CANCELED); -} - -void ExtensionReenabler::OnWebstoreRequestFailure() { - Finish(ABORTED); -} - -void ExtensionReenabler::OnWebstoreResponseParseSuccess( - std::unique_ptr<base::DictionaryValue> webstore_data) { - DCHECK(!referrer_url_.is_empty()); - std::string error; - if (!WebstoreInlineInstaller::IsRequestorPermitted(*webstore_data, - referrer_url_, - &error)) { - Finish(NOT_ALLOWED); - } else { - ExtensionInstallPrompt::PromptType type = - ExtensionInstallPrompt::GetReEnablePromptTypeForExtension( - browser_context_, extension_.get()); - install_prompt_->ShowDialog( - base::Bind(&ExtensionReenabler::OnInstallPromptDone, - weak_factory_.GetWeakPtr()), - extension_.get(), nullptr, - std::make_unique<ExtensionInstallPrompt::Prompt>(type), - show_dialog_callback_); - } -} - -void ExtensionReenabler::OnWebstoreResponseParseFailure( - const std::string& error) { - Finish(ABORTED); -} - -void ExtensionReenabler::Finish(ReenableResult result) { - DCHECK(!finished_); - finished_ = true; - registry_observer_.RemoveAll(); - callback_.Run(result); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/extension_reenabler.h b/chrome/browser/extensions/extension_reenabler.h deleted file mode 100644 index 4c19f32..0000000 --- a/chrome/browser/extensions/extension_reenabler.h +++ /dev/null
@@ -1,130 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_REENABLER_H_ -#define CHROME_BROWSER_EXTENSIONS_EXTENSION_REENABLER_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/scoped_observer.h" -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/extensions/webstore_data_fetcher_delegate.h" -#include "extensions/browser/extension_registry_observer.h" - -namespace content { -class BrowserContext; -} - -namespace extensions { - -class Extension; -class ExtensionRegistry; -class WebstoreDataFetcher; - -// A class to handle reenabling an extension disabled due to a permissions -// increase. -// TODO(devlin): Once we get the UI figured out, we should also have this handle -// other disable reasons. -class ExtensionReenabler : public ExtensionRegistryObserver, - public WebstoreDataFetcherDelegate { - public: - enum ReenableResult { - REENABLE_SUCCESS, // The extension has been successfully re-enabled. - USER_CANCELED, // The user chose to not re-enable the extension. - NOT_ALLOWED, // The re-enable is not allowed. - ABORTED, // The re-enable process was aborted due to, e.g., - // shutdown or a bad webstore response. - }; - - using Callback = base::Callback<void(ReenableResult)>; - - ~ExtensionReenabler() override; - - // Prompts the user to reenable the given |extension|, and calls |callback| - // upon completion. - // If |referrer_url| is non-empty, then this will also check to make sure - // that the referrer_url is listed as a trusted url by the extension. - static std::unique_ptr<ExtensionReenabler> PromptForReenable( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - content::WebContents* web_contents, - const GURL& referrer_url, - const Callback& callback); - - // Like PromptForReenable, but allows tests to inject the - // ExtensionInstallPrompt. - static std::unique_ptr<ExtensionReenabler> - PromptForReenableWithCallbackForTest( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - const Callback& callback, - const ExtensionInstallPrompt::ShowDialogCallback& show_callback); - - private: - ExtensionReenabler( - const scoped_refptr<const Extension>& extension, - content::BrowserContext* browser_context, - const GURL& referrer_url, - const Callback& callback, - content::WebContents* web_contents, - const ExtensionInstallPrompt::ShowDialogCallback& show_callback); - - void OnInstallPromptDone(ExtensionInstallPrompt::Result result); - - // ExtensionRegistryObserver: - void OnExtensionLoaded(content::BrowserContext* browser_context, - const Extension* extension) override; - void OnExtensionUninstalled(content::BrowserContext* browser_context, - const Extension* extension, - UninstallReason reason) override; - - // WebstoreDataFetcherDelegate: - void OnWebstoreRequestFailure() override; - void OnWebstoreResponseParseSuccess( - std::unique_ptr<base::DictionaryValue> webstore_data) override; - void OnWebstoreResponseParseFailure(const std::string& error) override; - - // Sets the |finished_| bit and runs |callback_| with the given |result|. - void Finish(ReenableResult result); - - // The extension to be re-enabled. - scoped_refptr<const Extension> extension_; - - // The associated browser context. - content::BrowserContext* browser_context_; - - // The url of the referrer, if any. If this is non-empty, it means we have - // to check that the url is trusted by the extension. - GURL referrer_url_; - - // The callback to run upon completion. - Callback callback_; - - // The callback to use to show the dialog. - ExtensionInstallPrompt::ShowDialogCallback show_dialog_callback_; - - // The re-enable prompt. - std::unique_ptr<ExtensionInstallPrompt> install_prompt_; - - // Indicates whether the re-enable process finished. - bool finished_; - - // The data fetcher for retrieving webstore data. - std::unique_ptr<WebstoreDataFetcher> webstore_data_fetcher_; - - ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> - registry_observer_; - - base::WeakPtrFactory<ExtensionReenabler> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionReenabler); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_REENABLER_H_
diff --git a/chrome/browser/extensions/extension_reenabler_unittest.cc b/chrome/browser/extensions/extension_reenabler_unittest.cc deleted file mode 100644 index 2bc89df5..0000000 --- a/chrome/browser/extensions/extension_reenabler_unittest.cc +++ /dev/null
@@ -1,269 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <utility> - -#include "base/macros.h" -#include "base/run_loop.h" -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/extensions/extension_reenabler.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_service_test_base.h" -#include "chrome/browser/extensions/extension_system_factory.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/test/base/testing_profile.h" -#include "components/crx_file/id_util.h" -#include "extensions/browser/extension_dialog_auto_confirm.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/management_policy.h" -#include "extensions/browser/test_extensions_browser_client.h" -#include "extensions/common/extension.h" -#include "extensions/common/extension_builder.h" -#include "extensions/common/value_builder.h" - -namespace extensions { - -namespace { - -// A simple provider that says all extensions must remain disabled. -class TestManagementProvider : public ManagementPolicy::Provider { - public: - TestManagementProvider() {} - ~TestManagementProvider() override {} - - private: - // MananagementPolicy::Provider: - std::string GetDebugPolicyProviderName() const override { return "test"; } - bool MustRemainDisabled(const Extension* extension, - disable_reason::DisableReason* reason, - base::string16* error) const override { - return true; - } - - DISALLOW_COPY_AND_ASSIGN(TestManagementProvider); -}; - -// A helper class for all the various callbacks associated with reenabling an -// extension. This class also helps store the results of the run. -class CallbackHelper { - public: - CallbackHelper() {} - ~CallbackHelper() {} - - // Get a callback to run on the completion of the reenable process and reset - // |result_|. - ExtensionReenabler::Callback GetCallback() { - result_.reset(); - return base::Bind(&CallbackHelper::OnComplete, - base::Unretained(this)); - } - - // Check if we have receved any result, and if it matches the expected one. - bool has_result() const { return result_ != nullptr; } - bool result_matches(ExtensionReenabler::ReenableResult expected) const { - return result_.get() && *result_ == expected; - } - - // Create a test ExtensionInstallPrompt that will not display any UI (which - // causes unit tests to crash), but rather runs the given |quit_closure| (with - // the prompt still active|. - ExtensionInstallPrompt::ShowDialogCallback CreateShowCallback( - base::OnceClosure quit_closure) { - quit_closure_ = std::move(quit_closure); - return base::Bind(&CallbackHelper::OnShow, base::Unretained(this)); - } - - private: - // The callback to run once the reenable process finishes. - void OnComplete(ExtensionReenabler::ReenableResult result) { - result_.reset(new ExtensionReenabler::ReenableResult(result)); - } - - // The callback to run when a test ExtensionInstallPrompt is ready to show. - void OnShow(ExtensionInstallPromptShowParams* show_params, - const ExtensionInstallPrompt::DoneCallback& done_callback, - std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt) { - DCHECK(quit_closure_); - std::move(quit_closure_).Run(); - } - - // The closure to quit the currently-running loop; used with test - // ExtensionInstallPrompts. - base::OnceClosure quit_closure_; - - // The result of the reenable process, or null if the process hasn't finished. - std::unique_ptr<ExtensionReenabler::ReenableResult> result_; - - DISALLOW_COPY_AND_ASSIGN(CallbackHelper); -}; - -} // namespace - -class ExtensionReenablerUnitTest : public ExtensionServiceTestBase { - public: - ExtensionReenablerUnitTest() {} - ~ExtensionReenablerUnitTest() override {} - - private: - void SetUp() override; - void TearDown() override; - - std::unique_ptr<TestExtensionsBrowserClient> test_browser_client_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionReenablerUnitTest); -}; - -void ExtensionReenablerUnitTest::SetUp() { - ExtensionServiceTestBase::SetUp(); - InitializeEmptyExtensionService(); - // We need a TestExtensionsBrowserClient because the real one tries to - // implicitly convert any browser context to a (non-Testing)Profile. - test_browser_client_.reset(new TestExtensionsBrowserClient(profile())); - test_browser_client_->set_extension_system_factory( - ExtensionSystemFactory::GetInstance()); - ExtensionsBrowserClient::Set(test_browser_client_.get()); -} - -void ExtensionReenablerUnitTest::TearDown() { - profile_.reset(); - ExtensionsBrowserClient::Set(nullptr); - test_browser_client_.reset(); - ExtensionServiceTestBase::TearDown(); -} - -// Test that the ExtensionReenabler reenables disabled extensions. -TEST_F(ExtensionReenablerUnitTest, TestReenablingDisabledExtension) { - // Create a simple extension and add it to the service. - scoped_refptr<const Extension> extension = - ExtensionBuilder() - .SetManifest(DictionaryBuilder() - .Set("name", "test ext") - .Set("version", "1.0") - .Set("manifest_version", 2) - .Set("description", "a test ext") - .Build()) - .SetID(crx_file::id_util::GenerateId("test ext")) - .Build(); - service()->AddExtension(extension.get()); - EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); - - CallbackHelper callback_helper; - - // Check that the ExtensionReenabler can re-enable disabled extensions. - { - // Disable the extension due to a permissions increase (the only type of - // disablement we handle with the ExtensionReenabler so far). - service()->DisableExtension(extension->id(), - disable_reason::DISABLE_PERMISSIONS_INCREASE); - // Sanity check that it's disabled. - EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); - - // Automatically confirm install prompts. - ScopedTestDialogAutoConfirm auto_confirm( - ScopedTestDialogAutoConfirm::ACCEPT); - - // Run the ExtensionReenabler. - std::unique_ptr<ExtensionReenabler> extension_reenabler = - ExtensionReenabler::PromptForReenable(extension, profile(), - nullptr, // No web contents. - GURL(), // No referrer. - callback_helper.GetCallback()); - base::RunLoop().RunUntilIdle(); - - // The extension should be enabled. - EXPECT_TRUE(registry()->enabled_extensions().Contains(extension->id())); - EXPECT_TRUE( - callback_helper.result_matches(ExtensionReenabler::REENABLE_SUCCESS)); - } - - // Check that we don't re-enable extensions that must remain disabled, and - // that the re-enabler reports failure correctly. - { - ScopedTestDialogAutoConfirm auto_confirm( - ScopedTestDialogAutoConfirm::ACCEPT); - - ManagementPolicy* management_policy = - ExtensionSystem::Get(browser_context())->management_policy(); - ASSERT_TRUE(management_policy); - TestManagementProvider test_provider; - management_policy->RegisterProvider(&test_provider); - service()->DisableExtension(extension->id(), - disable_reason::DISABLE_PERMISSIONS_INCREASE); - - std::unique_ptr<ExtensionReenabler> extension_reenabler = - ExtensionReenabler::PromptForReenable(extension, profile(), - nullptr, // No web contents. - GURL(), // No referrer. - callback_helper.GetCallback()); - base::RunLoop().RunUntilIdle(); - - // The extension should be enabled. - EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); - EXPECT_TRUE( - callback_helper.result_matches(ExtensionReenabler::NOT_ALLOWED)); - - management_policy->UnregisterProvider(&test_provider); - } - - // Check that canceling the re-enable prompt doesn't re-enable the extension. - { - // Disable it again, and try canceling the prompt. - service()->DisableExtension(extension->id(), - disable_reason::DISABLE_PERMISSIONS_INCREASE); - ScopedTestDialogAutoConfirm auto_confirm( - ScopedTestDialogAutoConfirm::CANCEL); - std::unique_ptr<ExtensionReenabler> extension_reenabler = - ExtensionReenabler::PromptForReenable(extension, profile(), - nullptr, // No web contents. - GURL(), // No referrer. - callback_helper.GetCallback()); - base::RunLoop().RunUntilIdle(); - - // The extension should remain disabled. - EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); - EXPECT_TRUE( - callback_helper.result_matches(ExtensionReenabler::USER_CANCELED)); - } - - // Test that if the extension is re-enabled while the prompt is active, the - // prompt exits and reports success. - { - base::RunLoop run_loop; - std::unique_ptr<ExtensionReenabler> extension_reenabler = - ExtensionReenabler::PromptForReenableWithCallbackForTest( - extension, profile(), callback_helper.GetCallback(), - callback_helper.CreateShowCallback(run_loop.QuitClosure())); - run_loop.Run(); - - // We shouldn't have any result yet (the user hasn't confirmed or canceled). - EXPECT_FALSE(callback_helper.has_result()); - - // Reenable the extension. This should count as a success for reenabling. - service()->GrantPermissionsAndEnableExtension(extension.get()); - EXPECT_TRUE( - callback_helper.result_matches(ExtensionReenabler::REENABLE_SUCCESS)); - } - - // Test that prematurely destroying the re-enable prompt doesn't crash and - // reports an "aborted" result. - { - // Disable again, and create another prompt. - service()->DisableExtension(extension->id(), - disable_reason::DISABLE_PERMISSIONS_INCREASE); - base::RunLoop run_loop; - std::unique_ptr<ExtensionReenabler> extension_reenabler = - ExtensionReenabler::PromptForReenableWithCallbackForTest( - extension, profile(), callback_helper.GetCallback(), - callback_helper.CreateShowCallback(run_loop.QuitClosure())); - run_loop.Run(); - EXPECT_FALSE(callback_helper.has_result()); - // Destroy the reenabler to simulate the owning context being shut down - // (e.g., the tab closing). - extension_reenabler.reset(); - EXPECT_TRUE( - callback_helper.result_matches(ExtensionReenabler::ABORTED)); - } -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc index acb3d78..68eb168f 100644 --- a/chrome/browser/extensions/tab_helper.cc +++ b/chrome/browser/extensions/tab_helper.cc
@@ -20,8 +20,6 @@ #include "chrome/browser/extensions/install_observer.h" #include "chrome/browser/extensions/install_tracker.h" #include "chrome/browser/extensions/install_tracker_factory.h" -#include "chrome/browser/extensions/webstore_inline_installer.h" -#include "chrome/browser/extensions/webstore_inline_installer_factory.h" #include "chrome/browser/installable/installable_metrics.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" @@ -70,75 +68,6 @@ namespace extensions { -// A helper class to watch the progress of inline installation and update the -// renderer. Owned by the TabHelper. -class TabHelper::InlineInstallObserver : public InstallObserver { - public: - InlineInstallObserver(TabHelper* tab_helper, - content::BrowserContext* browser_context, - const ExtensionId& extension_id, - bool observe_download_progress, - bool observe_install_stage) - : tab_helper_(tab_helper), - extension_id_(extension_id), - observe_download_progress_(observe_download_progress), - observe_install_stage_(observe_install_stage), - install_observer_(this) { - DCHECK(tab_helper); - DCHECK(observe_download_progress || observe_install_stage); - InstallTracker* install_tracker = - InstallTrackerFactory::GetForBrowserContext(browser_context); - if (install_tracker) - install_observer_.Add(install_tracker); - } - ~InlineInstallObserver() override {} - - private: - // InstallObserver: - void OnBeginExtensionDownload(const ExtensionId& extension_id) override { - SendInstallStageChangedMessage(extension_id, - api::webstore::INSTALL_STAGE_DOWNLOADING); - } - void OnDownloadProgress(const ExtensionId& extension_id, - int percent_downloaded) override { - if (observe_download_progress_ && extension_id == extension_id_) { - auto iter = - tab_helper_->inline_install_progress_listeners_.find(extension_id); - DCHECK(iter != tab_helper_->inline_install_progress_listeners_.end()); - iter->second->InlineInstallDownloadProgress(percent_downloaded); - } - } - void OnBeginCrxInstall(const ExtensionId& extension_id) override { - SendInstallStageChangedMessage(extension_id, - api::webstore::INSTALL_STAGE_INSTALLING); - } - void OnShutdown() override { install_observer_.RemoveAll(); } - - void SendInstallStageChangedMessage(const ExtensionId& extension_id, - api::webstore::InstallStage stage) { - if (observe_install_stage_ && extension_id == extension_id_) { - auto iter = - tab_helper_->inline_install_progress_listeners_.find(extension_id); - DCHECK(iter != tab_helper_->inline_install_progress_listeners_.end()); - iter->second->InlineInstallStageChanged(stage); - } - } - - // The owning TabHelper (guaranteed to be valid). - TabHelper* const tab_helper_; - - // The id of the extension to observe. - ExtensionId extension_id_; - - // Whether or not to observe download/install progress. - const bool observe_download_progress_; - const bool observe_install_stage_; - - ScopedObserver<InstallTracker, InstallObserver> install_observer_; - - DISALLOW_COPY_AND_ASSIGN(InlineInstallObserver); -}; - TabHelper::~TabHelper() { RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_)); } @@ -152,9 +81,7 @@ script_executor_( new ScriptExecutor(web_contents, &script_execution_observers_)), extension_action_runner_(new ExtensionActionRunner(web_contents)), - webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()), registry_observer_(this), - bindings_(web_contents, this), image_loader_ptr_factory_(this), weak_ptr_factory_(this) { // The ActiveTabPermissionManager requires a session ID; ensure this @@ -379,80 +306,6 @@ pending_web_app_action_ = NONE; } -void TabHelper::DoInlineInstall( - const std::string& webstore_item_id, - int listeners_mask, - mojom::InlineInstallProgressListenerPtr install_progress_listener, - DoInlineInstallCallback callback) { - content::RenderFrameHost* host = web_contents()->GetMainFrame(); - if (bindings_.GetCurrentTargetFrame() != host) { - NOTREACHED(); - return; - } - - GURL requestor_url(host->GetLastCommittedURL()); - // Check that the listener is reasonable. We should never get anything other - // than an install stage listener, a download listener, or both. - // The requestor_url should also be valid, and the renderer should disallow - // child frames from sending the IPC. - if ((listeners_mask & ~(api::webstore::INSTALL_STAGE_LISTENER | - api::webstore::DOWNLOAD_PROGRESS_LISTENER)) != 0 || - !requestor_url.is_valid() || requestor_url == url::kAboutBlankURL) { - NOTREACHED(); - return; - } - - if (base::ContainsKey(install_callbacks_, webstore_item_id)) { - std::move(callback).Run(false, webstore_install::kInstallInProgressError, - webstore_install::INSTALL_IN_PROGRESS); - return; - } - - install_callbacks_[webstore_item_id] = std::move(callback); - inline_install_progress_listeners_[webstore_item_id] = - std::move(install_progress_listener); - // Inform the Webstore API that an inline install is happening, in case the - // page requested status updates. - ExtensionRegistry* registry = ExtensionRegistry::Get(profile_); - if (registry->disabled_extensions().Contains(webstore_item_id) && - (ExtensionPrefs::Get(profile_)->GetDisableReasons(webstore_item_id) & - disable_reason::DISABLE_PERMISSIONS_INCREASE) != 0) { - // The extension was disabled due to permissions increase. Prompt for - // re-enable. - // TODO(devlin): We should also prompt for re-enable for other reasons, - // like user-disabled. - // For clarity, explicitly end any prior reenable process. - extension_reenabler_.reset(); - extension_reenabler_ = ExtensionReenabler::PromptForReenable( - registry->disabled_extensions().GetByID(webstore_item_id), profile_, - web_contents(), requestor_url, - base::Bind(&TabHelper::OnReenableComplete, - weak_ptr_factory_.GetWeakPtr(), webstore_item_id)); - } else { - // TODO(devlin): We should adddress the case of the extension already - // being installed and enabled. - bool observe_download_progress = - (listeners_mask & api::webstore::DOWNLOAD_PROGRESS_LISTENER) != 0; - bool observe_install_stage = - (listeners_mask & api::webstore::INSTALL_STAGE_LISTENER) != 0; - if (observe_install_stage || observe_download_progress) { - DCHECK_EQ(0u, install_observers_.count(webstore_item_id)); - install_observers_[webstore_item_id] = - std::make_unique<InlineInstallObserver>( - this, web_contents()->GetBrowserContext(), webstore_item_id, - observe_download_progress, observe_install_stage); - } - - WebstoreStandaloneInstaller::Callback callback = - base::Bind(&TabHelper::OnInlineInstallComplete, - weak_ptr_factory_.GetWeakPtr(), webstore_item_id); - scoped_refptr<WebstoreInlineInstaller> installer( - webstore_inline_installer_factory_->CreateInstaller( - web_contents(), host, webstore_item_id, requestor_url, callback)); - installer->BeginInstall(); - } -} - void TabHelper::OnGetAppInstallState(content::RenderFrameHost* host, const GURL& requestor_url, int return_route_id, @@ -514,11 +367,6 @@ } } -void TabHelper::SetWebstoreInlineInstallerFactoryForTests( - WebstoreInlineInstallerFactory* factory) { - webstore_inline_installer_factory_.reset(factory); -} - void TabHelper::OnImageLoaded(const gfx::Image& image) { if (!image.IsEmpty()) { extension_app_icon_ = *image.ToSkBitmap(); @@ -530,47 +378,6 @@ return ExtensionTabUtil::GetWindowControllerOfTab(web_contents()); } -void TabHelper::OnReenableComplete(const ExtensionId& extension_id, - ExtensionReenabler::ReenableResult result) { - // Map the re-enable results to webstore-install results. - webstore_install::Result webstore_result = webstore_install::SUCCESS; - std::string error; - switch (result) { - case ExtensionReenabler::REENABLE_SUCCESS: - break; // already set - case ExtensionReenabler::USER_CANCELED: - webstore_result = webstore_install::USER_CANCELLED; - error = "User canceled install."; - break; - case ExtensionReenabler::NOT_ALLOWED: - webstore_result = webstore_install::NOT_PERMITTED; - error = "Install not permitted."; - break; - case ExtensionReenabler::ABORTED: - webstore_result = webstore_install::ABORTED; - error = "Aborted due to tab closing."; - break; - } - - OnInlineInstallComplete(extension_id, - result == ExtensionReenabler::REENABLE_SUCCESS, error, - webstore_result); - // Note: ExtensionReenabler contained the callback with the curried-in - // |extension_id|; delete it last. - extension_reenabler_.reset(); -} - -void TabHelper::OnInlineInstallComplete(const ExtensionId& extension_id, - bool success, - const std::string& error, - webstore_install::Result result) { - install_observers_.erase(extension_id); - auto iter = install_callbacks_.find(extension_id); - DCHECK(iter != install_callbacks_.end()); - std::move(iter->second).Run(success, success ? std::string() : error, result); - install_callbacks_.erase(iter); -} - WebContents* TabHelper::GetAssociatedWebContents() const { return web_contents(); }
diff --git a/chrome/browser/extensions/tab_helper.h b/chrome/browser/extensions/tab_helper.h index 8fa70a9..0db580c 100644 --- a/chrome/browser/extensions/tab_helper.h +++ b/chrome/browser/extensions/tab_helper.h
@@ -15,9 +15,7 @@ #include "base/observer_list.h" #include "base/scoped_observer.h" #include "chrome/browser/extensions/active_tab_permission_granter.h" -#include "chrome/browser/extensions/extension_reenabler.h" #include "chrome/common/chrome_render_frame.mojom.h" -#include "chrome/common/extensions/mojom/inline_install.mojom.h" #include "chrome/common/extensions/webstore_install_result.h" #include "chrome/common/web_application_info.h" #include "content/public/browser/web_contents_binding_set.h" @@ -43,14 +41,12 @@ class ExtensionActionRunner; class BookmarkAppHelper; class Extension; -class WebstoreInlineInstallerFactory; // Per-tab extension helper. Also handles non-extension apps. class TabHelper : public content::WebContentsObserver, public ExtensionFunctionDispatcher::Delegate, public ExtensionRegistryObserver, - public content::WebContentsUserData<TabHelper>, - public mojom::InlineInstaller { + public content::WebContentsUserData<TabHelper> { public: ~TabHelper() override; @@ -101,14 +97,7 @@ return active_tab_permission_granter_.get(); } - // Sets the factory used to create inline webstore item installers. - // Used for testing. Takes ownership of the factory instance. - void SetWebstoreInlineInstallerFactoryForTests( - WebstoreInlineInstallerFactory* factory); - private: - class InlineInstallObserver; - // Utility function to invoke member functions on all relevant // ContentRulesRegistries. template <class Func> @@ -148,13 +137,6 @@ const Extension* extension, UnloadedExtensionReason reason) override; - // mojom::InlineInstall: - void DoInlineInstall( - const std::string& webstore_item_id, - int listeners_mask, - mojom::InlineInstallProgressListenerPtr install_progress_listener, - DoInlineInstallCallback callback) override; - // Message handlers. void OnDidGetWebApplicationInfo( chrome::mojom::ChromeRenderFrameAssociatedPtr chrome_render_frame, @@ -179,16 +161,6 @@ void OnImageLoaded(const gfx::Image& image); - // WebstoreStandaloneInstaller::Callback. - void OnInlineInstallComplete(const ExtensionId& extension_id, - bool success, - const std::string& error, - webstore_install::Result result); - - // ExtensionReenabler::Callback. - void OnReenableComplete(const ExtensionId& extension_id, - ExtensionReenabler::ReenableResult result); - // Requests application info for the specified page. This is an asynchronous // request. The delegate is notified by way of OnDidGetWebApplicationInfo when // the data is available. @@ -231,30 +203,9 @@ std::unique_ptr<BookmarkAppHelper> bookmark_app_helper_; - // Creates WebstoreInlineInstaller instances for inline install triggers. - std::unique_ptr<WebstoreInlineInstallerFactory> - webstore_inline_installer_factory_; - - // The reenable prompt for disabled extensions, if any. - std::unique_ptr<ExtensionReenabler> extension_reenabler_; - ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> registry_observer_; - // Map of InlineInstallObservers for inline installations that have progress - // listeners. - std::map<ExtensionId, std::unique_ptr<InlineInstallObserver>> - install_observers_; - - // Map of function callbacks that are invoked when the inline installation for - // a particular extension (hence ExtensionId) completes. - std::map<ExtensionId, DoInlineInstallCallback> install_callbacks_; - - content::WebContentsFrameBindingSet<mojom::InlineInstaller> bindings_; - - std::map<ExtensionId, mojom::InlineInstallProgressListenerPtr> - inline_install_progress_listeners_; - // Vend weak pointers that can be invalidated to stop in-progress loads. base::WeakPtrFactory<TabHelper> image_loader_ptr_factory_;
diff --git a/chrome/browser/extensions/webstore_data_fetcher.cc b/chrome/browser/extensions/webstore_data_fetcher.cc index 1b84b751..558b721 100644 --- a/chrome/browser/extensions/webstore_data_fetcher.cc +++ b/chrome/browser/extensions/webstore_data_fetcher.cc
@@ -43,10 +43,6 @@ WebstoreDataFetcher::~WebstoreDataFetcher() {} -void WebstoreDataFetcher::SetPostData(const std::string& data) { - post_data_ = data; -} - void WebstoreDataFetcher::Start( network::mojom::URLLoaderFactory* url_loader_factory) { GURL webstore_data_url(extension_urls::GetWebstoreItemJsonDataURL(id_)); @@ -86,14 +82,10 @@ resource_request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DISABLE_CACHE; resource_request->referrer = referrer_url_; - resource_request->method = post_data_.empty() ? "GET" : "POST"; + resource_request->method = "GET"; simple_url_loader_ = network::SimpleURLLoader::Create( std::move(resource_request), traffic_annotation); - if (!post_data_.empty()) - simple_url_loader_->AttachStringForUpload(post_data_, - "application/octet-stream"); - if (max_auto_retries_ > 0) { simple_url_loader_->SetRetryOptions( max_auto_retries_,
diff --git a/chrome/browser/extensions/webstore_data_fetcher.h b/chrome/browser/extensions/webstore_data_fetcher.h index af46509..1d1edae 100644 --- a/chrome/browser/extensions/webstore_data_fetcher.h +++ b/chrome/browser/extensions/webstore_data_fetcher.h
@@ -36,10 +36,6 @@ const std::string webstore_item_id); ~WebstoreDataFetcher(); - // Makes this request use a POST instead of GET, and sends |data| in the - // body of the request. If |data| is empty, this is a no-op. - void SetPostData(const std::string& data); - void Start(network::mojom::URLLoaderFactory* url_loader_factory); void set_max_auto_retries(int max_retries) {
diff --git a/chrome/browser/extensions/webstore_data_fetcher_delegate.cc b/chrome/browser/extensions/webstore_data_fetcher_delegate.cc index 21cab5d..72233ec 100644 --- a/chrome/browser/extensions/webstore_data_fetcher_delegate.cc +++ b/chrome/browser/extensions/webstore_data_fetcher_delegate.cc
@@ -11,8 +11,6 @@ const char WebstoreDataFetcherDelegate::kIdKey[] = "id"; const char WebstoreDataFetcherDelegate::kExternalInstallDefaultButtonKey[] = "external_install_default_button"; -const char WebstoreDataFetcherDelegate::kInlineInstallNotSupportedKey[] = - "inline_install_not_supported"; const char WebstoreDataFetcherDelegate::kLocalizedDescriptionKey[] = "localized_description"; const char WebstoreDataFetcherDelegate::kLocalizedNameKey[] = "localized_name";
diff --git a/chrome/browser/extensions/webstore_data_fetcher_delegate.h b/chrome/browser/extensions/webstore_data_fetcher_delegate.h index a541e59..bef9178 100644 --- a/chrome/browser/extensions/webstore_data_fetcher_delegate.h +++ b/chrome/browser/extensions/webstore_data_fetcher_delegate.h
@@ -33,7 +33,6 @@ static const char kIconUrlKey[]; static const char kIdKey[]; static const char kExternalInstallDefaultButtonKey[]; - static const char kInlineInstallNotSupportedKey[]; static const char kLocalizedDescriptionKey[]; static const char kLocalizedNameKey[]; static const char kManifestKey[];
diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc deleted file mode 100644 index b10f636..0000000 --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ /dev/null
@@ -1,318 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/webstore_inline_installer.h" - -#include <utility> - -#include "base/json/json_writer.h" -#include "base/strings/stringprintf.h" -#include "base/values.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/extensions/webstore_data_fetcher.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h" -#include "chrome/browser/safe_browsing/safe_browsing_service.h" -#include "chrome/browser/ui/browser_finder.h" -#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" -#include "chrome/common/pref_names.h" -#include "components/prefs/pref_service.h" -#include "components/safe_browsing/proto/csd.pb.h" -#include "content/public/browser/navigation_entry.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/web_contents.h" - -using content::WebContents; -using safe_browsing::SafeBrowsingNavigationObserverManager; -using safe_browsing::ReferrerChain; - -namespace { - -// The number of user gestures to trace back for CWS pings. -const int kExtensionReferrerUserGestureLimit = 2; -} - -namespace extensions { - -const char kInvalidWebstoreResponseError[] = - "Invalid Chrome Web Store response."; -const char kNoVerifiedSitesError[] = - "Inline installs can only be initiated for Chrome Web Store items that " - "have one or more verified sites."; -const char kNotFromVerifiedSitesError[] = - "Installs can only be initiated by one of the Chrome Web Store item's " - "verified sites."; -const char kInlineInstallSupportedError[] = - "Inline installation is not supported for this item. The user will be " - "redirected to the Chrome Web Store."; -const char kInitiatedFromPopupError[] = - "Inline installs can not be initiated from pop-up windows."; -const char kInitiatedFromFullscreenError[] = - "Inline installs can not be initiated from fullscreen."; - -WebstoreInlineInstaller::WebstoreInlineInstaller( - content::WebContents* web_contents, - content::RenderFrameHost* host, - const std::string& webstore_item_id, - const GURL& requestor_url, - const Callback& callback) - : WebstoreStandaloneInstaller( - webstore_item_id, - Profile::FromBrowserContext(web_contents->GetBrowserContext()), - callback), - content::WebContentsObserver(web_contents), - host_(host), - requestor_url_(requestor_url) {} - -WebstoreInlineInstaller::~WebstoreInlineInstaller() {} - -// static -bool WebstoreInlineInstaller::IsRequestorPermitted( - const base::DictionaryValue& webstore_data, - const GURL& requestor_url, - std::string* error) { - // Ensure that there is at least one verified site present. - const bool data_has_single_site = webstore_data.HasKey(kVerifiedSiteKey); - const bool data_has_site_list = webstore_data.HasKey(kVerifiedSitesKey); - if (!data_has_single_site && !data_has_site_list) { - *error = kNoVerifiedSitesError; - return false; - } - bool requestor_is_ok = false; - // Handle the deprecated single-site case. - if (!data_has_site_list) { - std::string verified_site; - if (!webstore_data.GetString(kVerifiedSiteKey, &verified_site)) { - *error = kInvalidWebstoreResponseError; - return false; - } - requestor_is_ok = IsRequestorURLInVerifiedSite(requestor_url, - verified_site); - } else { - const base::ListValue* verified_sites = NULL; - if (!webstore_data.GetList(kVerifiedSitesKey, &verified_sites)) { - *error = kInvalidWebstoreResponseError; - return false; - } - for (auto it = verified_sites->begin(); - it != verified_sites->end() && !requestor_is_ok; ++it) { - std::string verified_site; - if (!it->GetAsString(&verified_site)) { - *error = kInvalidWebstoreResponseError; - return false; - } - if (IsRequestorURLInVerifiedSite(requestor_url, verified_site)) { - requestor_is_ok = true; - } - } - } - if (!requestor_is_ok) { - *error = kNotFromVerifiedSitesError; - return false; - } - *error = ""; - return true; -} - -bool WebstoreInlineInstaller::SafeBrowsingNavigationEventsEnabled() const { - return SafeBrowsingNavigationObserverManager::IsEnabledAndReady(profile()); -} - -std::string WebstoreInlineInstaller::GetPostData() { - // web_contents() might return null during tab destruction. This object would - // also be destroyed shortly thereafter but check to be on the safe side. - if (!web_contents()) - return std::string(); - - // Report extra data only when SafeBrowsing is enabled and SB navigation - // observer is enabled for the current profile. - if (!profile()->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled) || - !SafeBrowsingNavigationEventsEnabled()) { - return std::string(); - } - - scoped_refptr<SafeBrowsingNavigationObserverManager> - navigation_observer_manager = g_browser_process->safe_browsing_service() - ->navigation_observer_manager(); - - ReferrerChain referrer_chain; - SafeBrowsingNavigationObserverManager::AttributionResult result = - navigation_observer_manager->IdentifyReferrerChainByWebContents( - web_contents(), kExtensionReferrerUserGestureLimit, &referrer_chain); - - // If the referrer chain is incomplete we'll append most recent navigations - // to referrer chain for diagnose purpose. This only happens if user is not - // in incognito mode and has opted into extended reporting to Scout reporting. - int recent_navigations_to_collect = - SafeBrowsingNavigationObserverManager::CountOfRecentNavigationsToAppend( - *profile(), result); - navigation_observer_manager->AppendRecentNavigations( - recent_navigations_to_collect, &referrer_chain); - safe_browsing::ExtensionWebStoreInstallRequest request; - request.mutable_referrer_chain()->Swap(&referrer_chain); - request.mutable_referrer_chain_options()->set_recent_navigations_to_collect( - recent_navigations_to_collect); - - return request.SerializeAsString(); -} - -bool WebstoreInlineInstaller::CheckRequestorAlive() const { - // The frame or tab may have gone away - cancel installation in that case. - return host_ != nullptr && web_contents() != nullptr && - chrome::FindBrowserWithWebContents(web_contents()) != nullptr; -} - -const GURL& WebstoreInlineInstaller::GetRequestorURL() const { - return requestor_url_; -} - -std::unique_ptr<ExtensionInstallPrompt::Prompt> -WebstoreInlineInstaller::CreateInstallPrompt() const { - std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt( - new ExtensionInstallPrompt::Prompt( - ExtensionInstallPrompt::INLINE_INSTALL_PROMPT)); - - // crbug.com/260742: Don't display the user count if it's zero. The reason - // it's zero is very often that the number isn't actually being counted - // (intentionally), which means that it's unlikely to be correct. - prompt->SetWebstoreData(localized_user_count(), - show_user_count(), - average_rating(), - rating_count()); - return prompt; -} - -bool WebstoreInlineInstaller::ShouldShowPostInstallUI() const { - return true; -} - -bool WebstoreInlineInstaller::ShouldShowAppInstalledBubble() const { - return true; -} - -WebContents* WebstoreInlineInstaller::GetWebContents() const { - return web_contents(); -} - -bool WebstoreInlineInstaller::CheckInlineInstallPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - Browser* browser = chrome::FindBrowserWithWebContents(web_contents()); - DCHECK(browser); - if (browser->is_type_popup()) { - *error = kInitiatedFromPopupError; - return false; - } - FullscreenController* controller = - browser->exclusive_access_manager()->fullscreen_controller(); - if (controller->IsTabFullscreen()) { - *error = kInitiatedFromFullscreenError; - return false; - } - // The store may not support inline installs for this item, in which case - // we open the store-provided redirect URL in a new tab and abort the - // installation process. - bool inline_install_not_supported = false; - if (webstore_data.HasKey(kInlineInstallNotSupportedKey) - && !webstore_data.GetBoolean(kInlineInstallNotSupportedKey, - &inline_install_not_supported)) { - *error = kInvalidWebstoreResponseError; - return false; - } - if (inline_install_not_supported) { - std::string redirect_url; - if (!webstore_data.GetString(kRedirectUrlKey, &redirect_url)) { - *error = kInvalidWebstoreResponseError; - return false; - } - web_contents()->OpenURL(content::OpenURLParams( - GURL(redirect_url), - content::Referrer::SanitizeForRequest( - GURL(redirect_url), - content::Referrer(web_contents()->GetURL(), - blink::kWebReferrerPolicyDefault)), - WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui::PAGE_TRANSITION_AUTO_BOOKMARK, false)); - *error = kInlineInstallSupportedError; - return false; - } - *error = ""; - return true; -} - -bool WebstoreInlineInstaller::CheckRequestorPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - return IsRequestorPermitted(webstore_data, requestor_url_, error); -} - -// -// Private implementation. -// - -void WebstoreInlineInstaller::DidFinishNavigation( - content::NavigationHandle* navigation_handle) { - if (navigation_handle->HasCommitted() && - !navigation_handle->IsSameDocument() && - (navigation_handle->GetRenderFrameHost() == host_ || - navigation_handle->IsInMainFrame())) { - host_ = nullptr; - } -} - -void WebstoreInlineInstaller::WebContentsDestroyed() { - AbortInstall(); -} - -// static -bool WebstoreInlineInstaller::IsRequestorURLInVerifiedSite( - const GURL& requestor_url, - const std::string& verified_site) { - // Turn the verified site into a URL that can be parsed by URLPattern. - // |verified_site| must follow the format: - // - // [scheme://]host[:port][/path/specifier] - // - // If scheme is omitted, URLPattern will match against either an - // HTTP or HTTPS requestor. If scheme is specified, it must be either HTTP - // or HTTPS, and URLPattern will only match the scheme specified. - GURL verified_site_url(verified_site); - int valid_schemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; - if (!verified_site_url.is_valid() || !verified_site_url.IsStandard()) - // If no scheme is specified, GURL will fail to parse the string correctly. - // It will either determine that the URL is invalid, or parse a - // host:port/path as scheme:host/path. - verified_site_url = GURL("http://" + verified_site); - else if (verified_site_url.SchemeIs("http")) - valid_schemes = URLPattern::SCHEME_HTTP; - else if (verified_site_url.SchemeIs("https")) - valid_schemes = URLPattern::SCHEME_HTTPS; - else - return false; - - std::string port_spec = - verified_site_url.has_port() ? ":" + verified_site_url.port() : ""; - std::string path_spec = verified_site_url.path() + "*"; - std::string verified_site_pattern_spec = - base::StringPrintf( - "%s://*.%s%s%s", - verified_site_url.scheme().c_str(), - verified_site_url.host().c_str(), - port_spec.c_str(), - path_spec.c_str()); - - URLPattern verified_site_pattern(valid_schemes); - URLPattern::ParseResult parse_result = - verified_site_pattern.Parse(verified_site_pattern_spec); - if (parse_result != URLPattern::ParseResult::kSuccess) { - DLOG(WARNING) << "Failed to parse '" << verified_site_pattern_spec - << "': " << URLPattern::GetParseResultString(parse_result); - return false; - } - verified_site_pattern.SetScheme("*"); - - return verified_site_pattern.MatchesURL(requestor_url); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_inline_installer.h b/chrome/browser/extensions/webstore_inline_installer.h deleted file mode 100644 index 5b99811..0000000 --- a/chrome/browser/extensions/webstore_inline_installer.h +++ /dev/null
@@ -1,89 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_ -#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_ - -#include <memory> -#include <string> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "chrome/browser/extensions/webstore_standalone_installer.h" -#include "content/public/browser/web_contents_observer.h" - -namespace content { -class WebContents; -} - -namespace extensions { - -// Manages inline installs requested by a page: downloads and parses metadata -// from the webstore, shows the install UI, starts the download once the user -// confirms, optionally transfers the user to the store if the "View details" -// link is clicked in the UI, shows the "App installed" bubble and the -// post-install UI after successful installation. -// -// Clients will be notified of success or failure via the |callback| argument -// passed into the constructor. -class WebstoreInlineInstaller : public WebstoreStandaloneInstaller, - public content::WebContentsObserver { - public: - typedef WebstoreStandaloneInstaller::Callback Callback; - - WebstoreInlineInstaller(content::WebContents* web_contents, - content::RenderFrameHost* host, - const std::string& webstore_item_id, - const GURL& requestor_url, - const Callback& callback); - - // Returns true if given |requestor_url| is a verified site according to the - // given |webstore_data|. - static bool IsRequestorPermitted(const base::DictionaryValue& webstore_data, - const GURL& requestor_url, - std::string* error); - - protected: - friend class base::RefCountedThreadSafe<WebstoreInlineInstaller>; - - ~WebstoreInlineInstaller() override; - - // Returns whether to use the new navigation event tracker. - virtual bool SafeBrowsingNavigationEventsEnabled() const; - - // Implementations WebstoreStandaloneInstaller Template Method's hooks. - std::string GetPostData() override; - bool CheckRequestorAlive() const override; - const GURL& GetRequestorURL() const override; - bool ShouldShowPostInstallUI() const override; - bool ShouldShowAppInstalledBubble() const override; - content::WebContents* GetWebContents() const override; - std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() - const override; - bool CheckInlineInstallPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; - bool CheckRequestorPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; - - private: - // content::WebContentsObserver interface implementation. - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override; - void WebContentsDestroyed() override; - - // Checks whether the install is initiated by a page in a verified site - // (which is at least a domain, but can also have a port or a path). - static bool IsRequestorURLInVerifiedSite(const GURL& requestor_url, - const std::string& verified_site); - - // This corresponds to the frame that initiated the install request. - content::RenderFrameHost* host_; - GURL requestor_url_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreInlineInstaller); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_H_
diff --git a/chrome/browser/extensions/webstore_inline_installer_browsertest.cc b/chrome/browser/extensions/webstore_inline_installer_browsertest.cc deleted file mode 100644 index e2a7da6c..0000000 --- a/chrome/browser/extensions/webstore_inline_installer_browsertest.cc +++ /dev/null
@@ -1,584 +0,0 @@ -// Copyright 2013 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/extensions/webstore_inline_installer.h" - -#include "base/json/json_reader.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/run_loop.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "build/build_config.h" -#include "chrome/browser/content_settings/host_content_settings_map_factory.h" -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/tab_helper.h" -#include "chrome/browser/extensions/webstore_inline_installer_factory.h" -#include "chrome/browser/extensions/webstore_installer_test.h" -#include "chrome/browser/extensions/webstore_standalone_installer.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/base/ui_test_utils.h" -#include "components/content_settings/core/browser/host_content_settings_map.h" -#include "components/prefs/pref_service.h" -#include "components/safe_browsing/features.h" -#include "components/safe_browsing/proto/csd.pb.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/browser_test_utils.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" -#include "extensions/common/permissions/permission_set.h" -#include "net/dns/mock_host_resolver.h" -#include "net/test/embedded_test_server/http_request.h" -#include "url/gurl.h" - -using content::WebContents; - -namespace extensions { - -namespace { - -const char kWebstoreDomain[] = "cws.com"; -const char kAppDomain[] = "app.com"; -const char kNonAppDomain[] = "nonapp.com"; -const char kTestExtensionId[] = "ecglahbcnmdpdciemllbhojghbkagdje"; -const char kTestDataPath[] = "extensions/api_test/webstore_inline_install"; -const char kCrxFilename[] = "extension.crx"; - -const char kRedirect1Domain[] = "redirect1.com"; -const char kRedirect2Domain[] = "redirect2.com"; - -// A struct for letting us store the actual parameters that were passed to -// the install callback. -struct InstallResult { - bool success = false; - std::string error; - webstore_install::Result result = webstore_install::RESULT_LAST; -}; - -} // namespace - -class WebstoreInlineInstallerTest : public WebstoreInstallerTest { - public: - WebstoreInlineInstallerTest() - : WebstoreInstallerTest( - kWebstoreDomain, - kTestDataPath, - kCrxFilename, - kAppDomain, - kNonAppDomain) {} -}; - -class ProgrammableInstallPrompt : public ExtensionInstallPrompt { - public: - explicit ProgrammableInstallPrompt(WebContents* contents) - : ExtensionInstallPrompt(contents) - {} - - ~ProgrammableInstallPrompt() override { g_done_callback = nullptr; } - - void ShowDialog( - const ExtensionInstallPrompt::DoneCallback& done_callback, - const Extension* extension, - const SkBitmap* icon, - std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt, - std::unique_ptr<const extensions::PermissionSet> custom_permissions, - const ShowDialogCallback& show_dialog_callback) override { - done_callback_ = done_callback; - g_done_callback = &done_callback_; - } - - static bool Ready() { return g_done_callback != nullptr; } - - static void Accept() { - g_done_callback->Run(ExtensionInstallPrompt::Result::ACCEPTED); - } - - static void Reject() { - g_done_callback->Run(ExtensionInstallPrompt::Result::USER_CANCELED); - } - - private: - static ExtensionInstallPrompt::DoneCallback* g_done_callback; - - ExtensionInstallPrompt::DoneCallback done_callback_; - - DISALLOW_COPY_AND_ASSIGN(ProgrammableInstallPrompt); -}; - -ExtensionInstallPrompt::DoneCallback* - ProgrammableInstallPrompt::g_done_callback = nullptr; - -// Fake inline installer which creates a programmable prompt in place of -// the normal dialog UI. -class WebstoreInlineInstallerForTest : public WebstoreInlineInstaller { - public: - WebstoreInlineInstallerForTest(WebContents* contents, - content::RenderFrameHost* host, - const std::string& extension_id, - const GURL& requestor_url, - const Callback& callback, - bool enable_safebrowsing_redirects) - : WebstoreInlineInstaller( - contents, - host, - kTestExtensionId, - requestor_url, - base::Bind(&WebstoreInlineInstallerForTest::InstallCallback, - base::Unretained(this))), - install_result_target_(nullptr), - enable_safebrowsing_redirects_(enable_safebrowsing_redirects), - programmable_prompt_(nullptr) {} - - std::unique_ptr<ExtensionInstallPrompt> CreateInstallUI() override { - programmable_prompt_ = new ProgrammableInstallPrompt(web_contents()); - return base::WrapUnique(programmable_prompt_); - } - - // Added here to make it public so that test cases can call it below. - bool CheckRequestorAlive() const override { - return WebstoreInlineInstaller::CheckRequestorAlive(); - } - - bool SafeBrowsingNavigationEventsEnabled() const override { - return enable_safebrowsing_redirects_; - } - - // Tests that care about the actual arguments to the install callback can use - // this to receive a copy in |install_result_target|. - void set_install_result_target( - std::unique_ptr<InstallResult>* install_result_target) { - install_result_target_ = install_result_target; - } - - private: - ~WebstoreInlineInstallerForTest() override {} - - friend class base::RefCountedThreadSafe<WebstoreStandaloneInstaller>; - - void InstallCallback(bool success, - const std::string& error, - webstore_install::Result result) { - if (install_result_target_) { - install_result_target_->reset(new InstallResult); - (*install_result_target_)->success = success; - (*install_result_target_)->error = error; - (*install_result_target_)->result = result; - } - } - - // This can be set by tests that want to get the actual install callback - // arguments. - std::unique_ptr<InstallResult>* install_result_target_; - - // This can be set by tests that want to use the new SafeBrowsing redirect - // tracker. - bool enable_safebrowsing_redirects_; - - ProgrammableInstallPrompt* programmable_prompt_; -}; - -class WebstoreInlineInstallerForTestFactory : - public WebstoreInlineInstallerFactory { - public: - WebstoreInlineInstallerForTestFactory() - : last_installer_(nullptr), enable_safebrowsing_redirects_(false) {} - explicit WebstoreInlineInstallerForTestFactory( - bool enable_safebrowsing_redirects) - : last_installer_(nullptr), - enable_safebrowsing_redirects_(enable_safebrowsing_redirects) {} - ~WebstoreInlineInstallerForTestFactory() override {} - - WebstoreInlineInstallerForTest* last_installer() { return last_installer_; } - - WebstoreInlineInstaller* CreateInstaller( - WebContents* contents, - content::RenderFrameHost* host, - const std::string& webstore_item_id, - const GURL& requestor_url, - const WebstoreStandaloneInstaller::Callback& callback) override { - last_installer_ = new WebstoreInlineInstallerForTest( - contents, host, webstore_item_id, requestor_url, callback, - enable_safebrowsing_redirects_); - return last_installer_; - } - - private: - // The last installer that was created. - WebstoreInlineInstallerForTest* last_installer_; - - bool enable_safebrowsing_redirects_; -}; - -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - CloseTabBeforeInstallConfirmation) { - GURL install_url = GenerateTestServerUrl(kAppDomain, "install.html"); - ui_test_utils::NavigateToURL(browser(), install_url); - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); - tab_helper->SetWebstoreInlineInstallerFactoryForTests( - new WebstoreInlineInstallerForTestFactory()); - RunTestAsync("runTest"); - while (!ProgrammableInstallPrompt::Ready()) - base::RunLoop().RunUntilIdle(); - web_contents->Close(); - ProgrammableInstallPrompt::Accept(); -} - -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - NavigateBeforeInstallConfirmation) { - GURL install_url = GenerateTestServerUrl(kAppDomain, "install.html"); - ui_test_utils::NavigateToURL(browser(), install_url); - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); - WebstoreInlineInstallerForTestFactory* factory = - new WebstoreInlineInstallerForTestFactory(); - tab_helper->SetWebstoreInlineInstallerFactoryForTests(factory); - RunTestAsync("runTest"); - while (!ProgrammableInstallPrompt::Ready()) - base::RunLoop().RunUntilIdle(); - GURL new_url = GenerateTestServerUrl(kNonAppDomain, "empty.html"); - web_contents->GetController().LoadURL( - new_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); - EXPECT_TRUE(content::WaitForLoadStop(web_contents)); - ASSERT_NE(factory->last_installer(), nullptr); - EXPECT_NE(factory->last_installer()->web_contents(), nullptr); - EXPECT_FALSE(factory->last_installer()->CheckRequestorAlive()); - - // Right now the way we handle navigations away from the frame that began the - // inline install is to just declare the requestor to be dead, but not to - // kill the prompt (that would be a better UX, but more complicated to - // implement). If we ever do change things to kill the prompt in this case, - // the following code can be removed (it verifies that clicking ok on the - // dialog does not result in an install). - std::unique_ptr<InstallResult> install_result; - factory->last_installer()->set_install_result_target(&install_result); - ASSERT_TRUE(ProgrammableInstallPrompt::Ready()); - ProgrammableInstallPrompt::Accept(); - ASSERT_NE(install_result.get(), nullptr); - EXPECT_EQ(install_result->success, false); - EXPECT_EQ(install_result->result, webstore_install::ABORTED); -} - -// Flaky: https://crbug.com/537526. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - DISABLED_ShouldBlockInlineInstallFromPopupWindow) { - GURL install_url = - GenerateTestServerUrl(kAppDomain, "install_from_popup.html"); - // Disable popup blocking for the test url. - HostContentSettingsMapFactory::GetForProfile(browser()->profile()) - ->SetContentSettingDefaultScope(install_url, GURL(), - CONTENT_SETTINGS_TYPE_POPUPS, - std::string(), CONTENT_SETTING_ALLOW); - ui_test_utils::NavigateToURL(browser(), install_url); - // The test page opens a popup which is a new |browser| window. - Browser* popup_browser = - chrome::FindLastActiveWithProfile(browser()->profile()); - WebContents* popup_contents = - popup_browser->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(base::ASCIIToUTF16("POPUP"), popup_contents->GetTitle()); - RunTest(popup_contents, "runTest"); -} - -// Allow inline install while in browser fullscreen mode. Browser fullscreen -// is initiated by the user using F11 (windows), ctrl+cmd+F (mac) or the green -// maximize window button on mac. This will be allowed since it cannot be -// initiated by an API and because of the nuance with mac windows. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - AllowInlineInstallFromFullscreenForBrowser) { - const GURL install_url = GenerateTestServerUrl(kAppDomain, "install.html"); - ui_test_utils::NavigateToURL(browser(), install_url); - AutoAcceptInstall(); - - // Enter browser fullscreen mode. - FullscreenController* controller = - browser()->exclusive_access_manager()->fullscreen_controller(); - controller->ToggleBrowserFullscreenMode(); - - RunTest("runTest"); - - // Ensure extension is installed. - ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); - EXPECT_TRUE( - registry->GenerateInstalledExtensionsSet()->Contains(kTestExtensionId)); -} - -// Prevent inline install while in tab fullscreen mode. Tab fullscreen is -// initiated using the browser API. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - BlockInlineInstallFromFullscreenForTab) { - const GURL install_url = - GenerateTestServerUrl(kAppDomain, "install_from_fullscreen.html"); - ui_test_utils::NavigateToURL(browser(), install_url); - AutoAcceptInstall(); - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - FullscreenController* controller = - browser()->exclusive_access_manager()->fullscreen_controller(); - - // Enter tab fullscreen mode. - controller->EnterFullscreenModeForTab(web_contents, install_url); - - RunTest("runTest"); - - // Ensure extension is not installed. - ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); - EXPECT_FALSE( - registry->GenerateInstalledExtensionsSet()->Contains(kTestExtensionId)); -} - -// Flaky on Linux ASan LSan (https://crbug.com/889804) -#if defined(OS_LINUX) && defined(ADDRESS_SANITIZER) -#define MAYBE_ReinstallDisabledExtension DISABLED_ReinstallDisabledExtension -#else -#define MAYBE_ReinstallDisabledExtension ReinstallDisabledExtension -#endif -// Ensure that inline-installing a disabled extension simply re-enables it. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, - MAYBE_ReinstallDisabledExtension) { - // Install an extension via inline install, and confirm it is successful. - AutoAcceptInstall(); - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install.html")); - RunTest("runTest"); - ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); - ASSERT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); - - // Disable the extension. - ExtensionService* extension_service = - ExtensionSystem::Get(browser()->profile())->extension_service(); - extension_service->DisableExtension(kTestExtensionId, - disable_reason::DISABLE_USER_ACTION); - EXPECT_TRUE(registry->disabled_extensions().GetByID(kTestExtensionId)); - - // Revisit the inline install site and reinstall the extension. It should - // simply be re-enabled, rather than try to install again. - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install.html")); - RunTest("runTest"); - EXPECT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); - // Since it was disabled by user action, the prompt should have just been the - // inline install prompt. - EXPECT_EQ(ExtensionInstallPrompt::INLINE_INSTALL_PROMPT, - ExtensionInstallPrompt::g_last_prompt_type_for_tests); - - // Disable the extension due to a permissions increase. - extension_service->DisableExtension( - kTestExtensionId, disable_reason::DISABLE_PERMISSIONS_INCREASE); - EXPECT_TRUE(registry->disabled_extensions().GetByID(kTestExtensionId)); - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install.html")); - RunTest("runTest"); - EXPECT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); - // The displayed prompt should be for the permissions increase, versus a - // normal inline install prompt. - EXPECT_EQ(ExtensionInstallPrompt::RE_ENABLE_PROMPT, - ExtensionInstallPrompt::g_last_prompt_type_for_tests); - - ExtensionInstallPrompt::g_last_prompt_type_for_tests = - ExtensionInstallPrompt::UNSET_PROMPT_TYPE; - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install.html")); - RunTest("runTest"); - // If the extension was already enabled, we should still display an inline - // install prompt (until we come up with something better). - EXPECT_EQ(ExtensionInstallPrompt::INLINE_INSTALL_PROMPT, - ExtensionInstallPrompt::g_last_prompt_type_for_tests); -} - -// Test calling chrome.webstore.install() twice without waiting for the first to -// finish. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerTest, DoubleInlineInstallTest) { - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "double_install.html")); - RunTest("runTest"); -} - -class WebstoreInlineInstallerRedirectTest - : public WebstoreInlineInstallerTest, - public ::testing::WithParamInterface<bool> { - public: - WebstoreInlineInstallerRedirectTest() : cws_request_received_(false) {} - ~WebstoreInlineInstallerRedirectTest() override {} - - void SetUpOnMainThread() override { - WebstoreInstallerTest::SetUpOnMainThread(); - host_resolver()->AddRule(kRedirect1Domain, "127.0.0.1"); - host_resolver()->AddRule(kRedirect2Domain, "127.0.0.1"); - } - - void ProcessServerRequest( - const net::test_server::HttpRequest& request) override { - cws_request_received_ = true; - if (request.content.empty()) - return; - - cws_request_proto_ = - std::make_unique<safe_browsing::ExtensionWebStoreInstallRequest>(); - if (!cws_request_proto_->ParseFromString(request.content)) - cws_request_proto_.reset(); - } - - bool cws_request_received_; - std::unique_ptr<safe_browsing::ExtensionWebStoreInstallRequest> - cws_request_proto_; -}; - -// Test that an install from a page arrived at via redirects includes the -// redirect information in the webstore request. -IN_PROC_BROWSER_TEST_P(WebstoreInlineInstallerRedirectTest, - IncludesRedirectProtoData) { - const bool using_safe_browsing_tracker = GetParam(); - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - TabHelper* tab_helper = TabHelper::FromWebContents(web_contents); - WebstoreInlineInstallerForTestFactory* factory = - new WebstoreInlineInstallerForTestFactory(using_safe_browsing_tracker); - tab_helper->SetWebstoreInlineInstallerFactoryForTests(factory); - - net::HostPortPair host_port = embedded_test_server()->host_port_pair(); - - std::string final_url = - GenerateTestServerUrl(kAppDomain, "install.html").spec(); - std::string redirect_url = - base::StringPrintf("http://%s:%d/server-redirect?%s", kRedirect2Domain, - host_port.port(), final_url.c_str()); - std::string install_url = - base::StringPrintf("http://%s:%d/server-redirect?%s", kRedirect1Domain, - host_port.port(), redirect_url.c_str()); - AutoAcceptInstall(); - ui_test_utils::NavigateToURL(browser(), GURL(install_url)); - - RunTestAsync("runTest"); - while (!ProgrammableInstallPrompt::Ready()) - base::RunLoop().RunUntilIdle(); - web_contents->Close(); - - EXPECT_TRUE(cws_request_received_); - if (!using_safe_browsing_tracker) { - ASSERT_EQ(nullptr, cws_request_proto_); - return; - } - ASSERT_NE(nullptr, cws_request_proto_); - ASSERT_EQ(1, cws_request_proto_->referrer_chain_size()); - - safe_browsing::ReferrerChainEntry referrer_entry = - cws_request_proto_->referrer_chain(0); - - // Check that the expected domains are in the redirect list. - const std::set<std::string> expected_redirect_domains = { - kRedirect1Domain, kRedirect2Domain, kAppDomain}; - - EXPECT_EQ(final_url, referrer_entry.url()); - EXPECT_EQ(safe_browsing::ReferrerChainEntry::CLIENT_REDIRECT, - referrer_entry.type()); - EXPECT_EQ(3, referrer_entry.server_redirect_chain_size()); - EXPECT_EQ(install_url, referrer_entry.server_redirect_chain(0).url()); - EXPECT_EQ(redirect_url, referrer_entry.server_redirect_chain(1).url()); - EXPECT_EQ(final_url, referrer_entry.server_redirect_chain(2).url()); - EXPECT_TRUE(cws_request_proto_->referrer_chain_options() - .has_recent_navigations_to_collect()); -} - -// Test that an install from a page arrived at via redirects does not include -// redirect information when SafeBrowsing is disabled. -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerRedirectTest, - NoRedirectDataWhenSafeBrowsingDisabled) { - PrefService* pref_service = browser()->profile()->GetPrefs(); - EXPECT_TRUE(pref_service->GetBoolean(prefs::kSafeBrowsingEnabled)); - - // Disable SafeBrowsing. - pref_service->SetBoolean(prefs::kSafeBrowsingEnabled, false); - - // Hand craft a url that will cause the test server to issue redirects. - const std::vector<std::string> redirects = {kRedirect1Domain, - kRedirect2Domain}; - net::HostPortPair host_port = embedded_test_server()->host_port_pair(); - std::string redirect_chain; - for (const auto& redirect : redirects) { - std::string redirect_url = base::StringPrintf( - "http://%s:%d/server-redirect?", redirect.c_str(), host_port.port()); - redirect_chain += redirect_url; - } - const GURL install_url = - GURL(redirect_chain + - GenerateTestServerUrl(kAppDomain, "install.html").spec()); - - AutoAcceptInstall(); - ui_test_utils::NavigateToURL(browser(), install_url); - RunTest("runTest"); - - EXPECT_TRUE(cws_request_received_); - ASSERT_EQ(nullptr, cws_request_proto_); -} - -INSTANTIATE_TEST_CASE_P(NetRedirectTracking, - WebstoreInlineInstallerRedirectTest, - testing::Values(false)); -INSTANTIATE_TEST_CASE_P(SafeBrowsingRedirectTracking, - WebstoreInlineInstallerRedirectTest, - testing::Values(true)); - -class WebstoreInlineInstallerListenerTest : public WebstoreInlineInstallerTest { - public: - WebstoreInlineInstallerListenerTest() {} - ~WebstoreInlineInstallerListenerTest() override {} - - protected: - void RunTest(const std::string& file_name) { - AutoAcceptInstall(); - ui_test_utils::NavigateToURL(browser(), - GenerateTestServerUrl(kAppDomain, file_name)); - WebstoreInstallerTest::RunTest("runTest"); - } -}; - -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerListenerTest, - InstallStageListenerTest) { - RunTest("install_stage_listener.html"); -} - -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerListenerTest, - DownloadProgressListenerTest) { - RunTest("download_progress_listener.html"); -} - -// Flaky on Linux ASan LSan (https://crbug.com/889804) -#if defined(OS_LINUX) && defined(ADDRESS_SANITIZER) -#define MAYBE_BothListenersTest DISABLED_BothListenersTest -#else -#define MAYBE_BothListenersTest BothListenersTest -#endif -IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallerListenerTest, - MAYBE_BothListenersTest) { - RunTest("both_listeners.html"); - // The extension should be installed. - ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); - EXPECT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); - - // Rinse and repeat: uninstall the extension, open a new tab, and install it - // again. Regression test for crbug.com/613949. - extension_service()->UninstallExtension( - kTestExtensionId, UNINSTALL_REASON_FOR_TESTING, nullptr); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(registry->enabled_extensions().GetByID(kTestExtensionId)); - int old_tab_index = browser()->tab_strip_model()->active_index(); - ui_test_utils::NavigateToURLWithDisposition( - browser(), GenerateTestServerUrl(kAppDomain, "both_listeners.html"), - WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - DCHECK_NE(old_tab_index, browser()->tab_strip_model()->active_index()); - browser()->tab_strip_model()->CloseWebContentsAt(old_tab_index, - TabStripModel::CLOSE_NONE); - WebstoreInstallerTest::RunTest("runTest"); - EXPECT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_inline_installer_factory.cc b/chrome/browser/extensions/webstore_inline_installer_factory.cc deleted file mode 100644 index 39e0b83a..0000000 --- a/chrome/browser/extensions/webstore_inline_installer_factory.cc +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2013 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/extensions/webstore_inline_installer_factory.h" - -#include <memory> - -#include "chrome/browser/extensions/webstore_inline_installer.h" -#include "content/public/browser/web_contents.h" - -namespace extensions { - -WebstoreInlineInstaller* WebstoreInlineInstallerFactory::CreateInstaller( - content::WebContents* contents, - content::RenderFrameHost* host, - const std::string& webstore_item_id, - const GURL& requestor_url, - const WebstoreStandaloneInstaller::Callback& callback) { - return new WebstoreInlineInstaller(contents, host, webstore_item_id, - requestor_url, callback); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_inline_installer_factory.h b/chrome/browser/extensions/webstore_inline_installer_factory.h deleted file mode 100644 index 97dcc8f9..0000000 --- a/chrome/browser/extensions/webstore_inline_installer_factory.h +++ /dev/null
@@ -1,39 +0,0 @@ -// Copyright 2013 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_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_FACTORY_H_ -#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_FACTORY_H_ - -#include <memory> -#include <string> - -#include "chrome/browser/extensions/extension_install_prompt.h" -#include "chrome/browser/extensions/webstore_standalone_installer.h" - -namespace content { -class WebContents; -} - -class GURL; - -namespace extensions { - -class WebstoreInlineInstaller; - -class WebstoreInlineInstallerFactory { - public: - virtual ~WebstoreInlineInstallerFactory() {} - - // Create a new WebstoreInlineInstallerInstance to be owned by the caller. - virtual WebstoreInlineInstaller* CreateInstaller( - content::WebContents* contents, - content::RenderFrameHost* host, - const std::string& webstore_item_id, - const GURL& requestor_url, - const WebstoreStandaloneInstaller::Callback& callback); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INLINE_INSTALLER_FACTORY_H_
diff --git a/chrome/browser/extensions/webstore_inline_installer_unittest.cc b/chrome/browser/extensions/webstore_inline_installer_unittest.cc deleted file mode 100644 index 881528bc..0000000 --- a/chrome/browser/extensions/webstore_inline_installer_unittest.cc +++ /dev/null
@@ -1,228 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <utility> -#include <vector> - -#include "chrome/browser/extensions/webstore_inline_installer.h" -#include "chrome/common/extensions/webstore_install_result.h" -#include "chrome/test/base/chrome_render_view_host_test_harness.h" -#include "content/public/browser/web_contents.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace extensions { - -namespace { - -// Wraps WebstoreInlineInstaller to provide access to domain verification -// methods for testing. -class TestWebstoreInlineInstaller : public WebstoreInlineInstaller { - public: - explicit TestWebstoreInlineInstaller(content::WebContents* contents, - const std::string& requestor_url); - - bool TestCheckRequestorPermitted(const base::DictionaryValue& webstore_data) { - std::string error; - return CheckRequestorPermitted(webstore_data, &error); - } - - protected: - ~TestWebstoreInlineInstaller() override; -}; - -void TestInstallerCallback(bool success, - const std::string& error, - webstore_install::Result result) {} - -TestWebstoreInlineInstaller::TestWebstoreInlineInstaller( - content::WebContents* contents, - const std::string& requestor_url) - : WebstoreInlineInstaller(contents, - contents->GetMainFrame(), - "", - GURL(requestor_url), - base::Bind(&TestInstallerCallback)) { -} - -TestWebstoreInlineInstaller::~TestWebstoreInlineInstaller() {} - -// We inherit from ChromeRenderViewHostTestHarness only for -// CreateTestWebContents, because we need a mock WebContents to support the -// underlying WebstoreInlineInstaller in each test case. -class WebstoreInlineInstallerTest : public ChromeRenderViewHostTestHarness { - public: - // testing::Test - void SetUp() override; - void TearDown() override; - - bool TestSingleVerifiedSite(const std::string& requestor_url, - const std::string& verified_site); - - bool TestMultipleVerifiedSites( - const std::string& requestor_url, - const std::vector<std::string>& verified_sites); - - protected: - std::unique_ptr<content::WebContents> web_contents_; -}; - -void WebstoreInlineInstallerTest::SetUp() { - ChromeRenderViewHostTestHarness::SetUp(); - web_contents_ = CreateTestWebContents(); -} - -void WebstoreInlineInstallerTest::TearDown() { - web_contents_.reset(NULL); - ChromeRenderViewHostTestHarness::TearDown(); -} - -// Simulates a test against the verified site string from a Webstore item's -// "verified_site" manifest entry. -bool WebstoreInlineInstallerTest::TestSingleVerifiedSite( - const std::string& requestor_url, - const std::string& verified_site) { - base::DictionaryValue webstore_data; - webstore_data.SetString("verified_site", verified_site); - - scoped_refptr<TestWebstoreInlineInstaller> installer = - new TestWebstoreInlineInstaller(web_contents_.get(), requestor_url); - return installer->TestCheckRequestorPermitted(webstore_data); -} - -// Simulates a test against a list of verified site strings from a Webstore -// item's "verified_sites" manifest entry. -bool WebstoreInlineInstallerTest::TestMultipleVerifiedSites( - const std::string& requestor_url, - const std::vector<std::string>& verified_sites) { - auto sites = std::make_unique<base::ListValue>(); - for (auto it = verified_sites.begin(); it != verified_sites.end(); ++it) { - sites->AppendString(*it); - } - base::DictionaryValue webstore_data; - webstore_data.Set("verified_sites", std::move(sites)); - - scoped_refptr<TestWebstoreInlineInstaller> installer = - new TestWebstoreInlineInstaller(web_contents_.get(), requestor_url); - return installer->TestCheckRequestorPermitted(webstore_data); -} - -} // namespace - -TEST_F(WebstoreInlineInstallerTest, DomainVerification) { - // Exact domain match. - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com", "example.com")); - - // The HTTPS scheme is allowed. - EXPECT_TRUE(TestSingleVerifiedSite("https://example.com", "example.com")); - - // The file: scheme is not allowed. - EXPECT_FALSE(TestSingleVerifiedSite("file:///example.com", "example.com")); - - // Trailing slash in URL. - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com/", "example.com")); - - // Page on the domain. - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com/page.html", - "example.com")); - - // Page on a subdomain. - EXPECT_TRUE(TestSingleVerifiedSite("http://sub.example.com/page.html", - "example.com")); - - // Root domain when only a subdomain is verified. - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com/", - "sub.example.com")); - - // Different subdomain when only a subdomain is verified. - EXPECT_FALSE(TestSingleVerifiedSite("http://www.example.com/", - "sub.example.com")); - - // Port matches. - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com:123/", - "example.com:123")); - - // Port doesn't match. - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com:456/", - "example.com:123")); - - // Port is missing in the requestor URL. - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com/", - "example.com:123")); - - // Port is missing in the verified site (any port matches). - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com:123/", "example.com")); - - // Path matches. - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com/path", - "example.com/path")); - - // Path doesn't match. - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com/foo", - "example.com/path")); - - // Path is missing. - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com", - "example.com/path")); - - // Path matches (with trailing slash). - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com/path/", - "example.com/path")); - - // Path matches (is a file under the path). - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com/path/page.html", - "example.com/path")); - - // Path and port match. - EXPECT_TRUE(TestSingleVerifiedSite( - "http://example.com:123/path/page.html", "example.com:123/path")); - - // Match specific valid schemes - EXPECT_TRUE(TestSingleVerifiedSite("http://example.com", - "http://example.com")); - EXPECT_TRUE(TestSingleVerifiedSite("https://example.com", - "https://example.com")); - - // Mismatch specific vaild schemes - EXPECT_FALSE(TestSingleVerifiedSite("https://example.com", - "http://example.com")); - EXPECT_FALSE(TestSingleVerifiedSite("http://example.com", - "https://example.com")); - - // Invalid scheme spec - EXPECT_FALSE(TestSingleVerifiedSite("file://example.com", - "file://example.com")); - - std::vector<std::string> verified_sites; - verified_sites.push_back("foo.example.com"); - verified_sites.push_back("bar.example.com:123"); - verified_sites.push_back("example.com/unicorns"); - - // Test valid examples against the site list. - - EXPECT_TRUE(TestMultipleVerifiedSites("http://foo.example.com", - verified_sites)); - - EXPECT_TRUE(TestMultipleVerifiedSites("http://bar.example.com:123", - verified_sites)); - - EXPECT_TRUE(TestMultipleVerifiedSites( - "http://cooking.example.com/unicorns/bacon.html", verified_sites)); - - // Test invalid examples against the site list. - - EXPECT_FALSE(TestMultipleVerifiedSites("http://example.com", - verified_sites)); - - EXPECT_FALSE(TestMultipleVerifiedSites("file://foo.example.com", - verified_sites)); - - EXPECT_FALSE(TestMultipleVerifiedSites("http://baz.example.com", - verified_sites)); - - EXPECT_FALSE(TestMultipleVerifiedSites("http://bar.example.com:456", - verified_sites)); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_install_with_prompt.cc b/chrome/browser/extensions/webstore_install_with_prompt.cc index aab742f..4e60cd55 100644 --- a/chrome/browser/extensions/webstore_install_with_prompt.cc +++ b/chrome/browser/extensions/webstore_install_with_prompt.cc
@@ -50,10 +50,6 @@ return !parent_window_tracker_->WasNativeWindowClosed(); } -const GURL& WebstoreInstallWithPrompt::GetRequestorURL() const { - return dummy_requestor_url_; -} - std::unique_ptr<ExtensionInstallPrompt::Prompt> WebstoreInstallWithPrompt::CreateInstallPrompt() const { return std::make_unique<ExtensionInstallPrompt::Prompt>( @@ -79,20 +75,4 @@ return dummy_web_contents_.get(); } -bool WebstoreInstallWithPrompt::CheckInlineInstallPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - // Assume the requestor is trusted. - *error = std::string(); - return true; -} - -bool WebstoreInstallWithPrompt::CheckRequestorPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - // Assume the requestor is trusted. - *error = std::string(); - return true; -} - } // namespace extensions
diff --git a/chrome/browser/extensions/webstore_install_with_prompt.h b/chrome/browser/extensions/webstore_install_with_prompt.h index ce4976ad..483ad49 100644 --- a/chrome/browser/extensions/webstore_install_with_prompt.h +++ b/chrome/browser/extensions/webstore_install_with_prompt.h
@@ -52,23 +52,16 @@ // extensions::WebstoreStandaloneInstaller overrides: bool CheckRequestorAlive() const override; - const GURL& GetRequestorURL() const override; bool ShouldShowPostInstallUI() const override; bool ShouldShowAppInstalledBubble() const override; content::WebContents* GetWebContents() const override; std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() const override; std::unique_ptr<ExtensionInstallPrompt> CreateInstallUI() override; - bool CheckInlineInstallPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; - bool CheckRequestorPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; private: bool show_post_install_ui_; - GURL dummy_requestor_url_; - // A non-visible WebContents used to download data from the webstore. std::unique_ptr<content::WebContents> dummy_web_contents_;
diff --git a/chrome/browser/extensions/webstore_installer.h b/chrome/browser/extensions/webstore_installer.h index ce7e85f..2dc86e3a 100644 --- a/chrome/browser/extensions/webstore_installer.h +++ b/chrome/browser/extensions/webstore_installer.h
@@ -58,6 +58,9 @@ enum InstallSource { // Inline installs trigger slightly different behavior (install source // is different, download referrers are the item's page in the gallery). + // TODO(ackermanb): Remove once server side metrics (omaha) tracking with + // this enum is figured out with any of the subclasses of + // WebstoreStandaloneInstaller. INSTALL_SOURCE_INLINE, INSTALL_SOURCE_APP_LAUNCHER, INSTALL_SOURCE_OTHER
diff --git a/chrome/browser/extensions/webstore_installer_test.cc b/chrome/browser/extensions/webstore_installer_test.cc index 7bdca8c9..8318d22 100644 --- a/chrome/browser/extensions/webstore_installer_test.cc +++ b/chrome/browser/extensions/webstore_installer_test.cc
@@ -8,8 +8,6 @@ #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/tab_helper.h" -#include "chrome/browser/extensions/webstore_inline_installer.h" -#include "chrome/browser/extensions/webstore_inline_installer_factory.h" #include "chrome/browser/extensions/webstore_installer_test.h" #include "chrome/browser/extensions/webstore_standalone_installer.h" #include "chrome/browser/profiles/profile.h" @@ -33,8 +31,6 @@ using content::WebContents; using extensions::Extension; using extensions::TabHelper; -using extensions::WebstoreInlineInstaller; -using extensions::WebstoreInlineInstallerFactory; using extensions::WebstoreStandaloneInstaller; using net::test_server::HttpRequest;
diff --git a/chrome/browser/extensions/webstore_installer_unittest.cc b/chrome/browser/extensions/webstore_installer_unittest.cc index 3befa733..9adee7ab 100644 --- a/chrome/browser/extensions/webstore_installer_unittest.cc +++ b/chrome/browser/extensions/webstore_installer_unittest.cc
@@ -26,8 +26,8 @@ TEST(WebstoreInstallerTest, PlatformParams) { std::string id = crx_file::id_util::GenerateId("some random string"); std::string source = "inline"; - GURL url = WebstoreInstaller::GetWebstoreInstallURL(id, - WebstoreInstaller::INSTALL_SOURCE_INLINE); + GURL url = WebstoreInstaller::GetWebstoreInstallURL( + id, WebstoreInstaller::INSTALL_SOURCE_INLINE); std::string query = url.query(); EXPECT_TRUE( Contains(query, StringPrintf("os=%s", UpdateQueryParams::GetOS())));
diff --git a/chrome/browser/extensions/webstore_reinstaller.cc b/chrome/browser/extensions/webstore_reinstaller.cc index 720b69e6..61fcf8eb 100644 --- a/chrome/browser/extensions/webstore_reinstaller.cc +++ b/chrome/browser/extensions/webstore_reinstaller.cc
@@ -43,10 +43,6 @@ return web_contents() != NULL; } -const GURL& WebstoreReinstaller::GetRequestorURL() const { - return GURL::EmptyGURL(); -} - std::unique_ptr<ExtensionInstallPrompt::Prompt> WebstoreReinstaller::CreateInstallPrompt() const { std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt( @@ -71,18 +67,6 @@ return web_contents(); } -bool WebstoreReinstaller::CheckInlineInstallPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - return true; -} - -bool WebstoreReinstaller::CheckRequestorPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const { - return true; -} - void WebstoreReinstaller::WebContentsDestroyed() { // Run the callback now, because AbortInstall() doesn't do it. RunCallback(false, kTabClosed, webstore_install::ABORTED);
diff --git a/chrome/browser/extensions/webstore_reinstaller.h b/chrome/browser/extensions/webstore_reinstaller.h index 19996fcf..24904a9b 100644 --- a/chrome/browser/extensions/webstore_reinstaller.h +++ b/chrome/browser/extensions/webstore_reinstaller.h
@@ -30,16 +30,11 @@ // WebstoreStandaloneInstaller: bool CheckRequestorAlive() const override; - const GURL& GetRequestorURL() const override; bool ShouldShowPostInstallUI() const override; bool ShouldShowAppInstalledBubble() const override; content::WebContents* GetWebContents() const override; std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() const override; - bool CheckInlineInstallPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; - bool CheckRequestorPermitted(const base::DictionaryValue& webstore_data, - std::string* error) const override; void OnInstallPromptDone(ExtensionInstallPrompt::Result result) override; // content::WebContentsObserver:
diff --git a/chrome/browser/extensions/webstore_standalone_installer.cc b/chrome/browser/extensions/webstore_standalone_installer.cc index a4499af4..e8517e9 100644 --- a/chrome/browser/extensions/webstore_standalone_installer.cc +++ b/chrome/browser/extensions/webstore_standalone_installer.cc
@@ -38,8 +38,7 @@ install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE), show_user_count_(true), average_rating_(0.0), - rating_count_(0) { -} + rating_count_(0) {} void WebstoreStandaloneInstaller::BeginInstall() { // Add a ref to keep this alive for WebstoreDataFetcher. @@ -63,10 +62,7 @@ // Use the requesting page as the referrer both since that is more correct // (it is the page that caused this request to happen) and so that we can // track top sites that trigger inline install requests. - webstore_data_fetcher_.reset( - new WebstoreDataFetcher(this, GetRequestorURL(), id_)); - - webstore_data_fetcher_->SetPostData(GetPostData()); + webstore_data_fetcher_.reset(new WebstoreDataFetcher(this, GURL(), id_)); webstore_data_fetcher_->Start( content::BrowserContext::GetDefaultStoragePartition(profile_) @@ -155,10 +151,6 @@ return localized_extension_for_display_.get(); } -std::string WebstoreStandaloneInstaller::GetPostData() { - return std::string(); -} - void WebstoreStandaloneInstaller::OnManifestParsed() { ProceedWithInstallPrompt(); } @@ -243,16 +235,6 @@ std::string error; - if (!CheckInlineInstallPermitted(*webstore_data, &error)) { - CompleteInstall(webstore_install::NOT_PERMITTED, error); - return; - } - - if (!CheckRequestorPermitted(*webstore_data, &error)) { - CompleteInstall(webstore_install::NOT_PERMITTED, error); - return; - } - // Manifest, number of users, average rating and rating count are required. std::string manifest; if (!webstore_data->GetString(kManifestKey, &manifest) ||
diff --git a/chrome/browser/extensions/webstore_standalone_installer.h b/chrome/browser/extensions/webstore_standalone_installer.h index d2989a1..c52695c 100644 --- a/chrome/browser/extensions/webstore_standalone_installer.h +++ b/chrome/browser/extensions/webstore_standalone_installer.h
@@ -20,8 +20,6 @@ #include "net/url_request/url_fetcher_delegate.h" #include "third_party/skia/include/core/SkBitmap.h" -class GURL; - namespace base { class DictionaryValue; } @@ -87,20 +85,11 @@ // Template Method's hooks to be implemented by subclasses. - // Gives subclasses an opportunity to provide extra post data in the form of - // serialized proto to the webstore data request before sending. The default - // implementation returns an empty string. - virtual std::string GetPostData(); - // Called at certain check points of the workflow to decide whether it makes // sense to proceed with installation. A requestor can be a website that // initiated an inline installation, or a command line option. virtual bool CheckRequestorAlive() const = 0; - // Requestor's URL, if any. Should be an empty GURL if URL is meaningless - // (e.g. for a command line option). - virtual const GURL& GetRequestorURL() const = 0; - // Should a new tab be opened after installation to show the newly installed // extension's icon? virtual bool ShouldShowPostInstallUI() const = 0; @@ -120,20 +109,6 @@ virtual std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() const = 0; - // Perform all necessary checks to make sure inline install is permitted, - // e.g. in the extension's properties in the store. The implementation may - // choose to ignore such properties. - virtual bool CheckInlineInstallPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const = 0; - - // Perform all necessary checks to make sure that requestor is allowed to - // initiate this install (e.g. that the requestor's URL matches the verified - // author's site specified in the extension's properties in the store). - virtual bool CheckRequestorPermitted( - const base::DictionaryValue& webstore_data, - std::string* error) const = 0; - // Will be called after the extension's manifest has been successfully parsed. // Subclasses can perform asynchronous checks at this point and call // ProceedWithInstallPrompt() to proceed with the install or otherwise call
diff --git a/chrome/browser/extensions/webstore_startup_installer.cc b/chrome/browser/extensions/webstore_startup_installer.cc deleted file mode 100644 index 010821a..0000000 --- a/chrome/browser/extensions/webstore_startup_installer.cc +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/extensions/webstore_startup_installer.h" - -#include <memory> - -namespace extensions { - -WebstoreStartupInstaller::WebstoreStartupInstaller( - const std::string& webstore_item_id, - Profile* profile, - bool show_prompt, - const Callback& callback) - : WebstoreInstallWithPrompt(webstore_item_id, profile, callback), - show_prompt_(show_prompt) { - set_install_source(WebstoreInstaller::INSTALL_SOURCE_INLINE); - set_show_post_install_ui(false); -} - -WebstoreStartupInstaller::~WebstoreStartupInstaller() {} - -std::unique_ptr<ExtensionInstallPrompt::Prompt> -WebstoreStartupInstaller::CreateInstallPrompt() const { - if (show_prompt_) { - return std::make_unique<ExtensionInstallPrompt::Prompt>( - ExtensionInstallPrompt::INSTALL_PROMPT); - } - return NULL; -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/webstore_startup_installer.h b/chrome/browser/extensions/webstore_startup_installer.h deleted file mode 100644 index 594582d6..0000000 --- a/chrome/browser/extensions/webstore_startup_installer.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_ -#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_ - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "chrome/browser/extensions/webstore_install_with_prompt.h" - -namespace extensions { - -// Manages inline installs requested to be performed at startup, e.g. via a -// command line option: downloads and parses metadata from the webstore, -// optionally shows an install UI, starts the download once the user -// confirms. -// -// Clients will be notified of success or failure via the |callback| argument -// passed into the constructor. -class WebstoreStartupInstaller : public WebstoreInstallWithPrompt { - public: - WebstoreStartupInstaller(const std::string& webstore_item_id, - Profile* profile, - bool show_prompt, - const Callback& callback); - - protected: - friend class base::RefCountedThreadSafe<WebstoreStartupInstaller>; - FRIEND_TEST_ALL_PREFIXES(WebstoreStartupInstallerTest, DomainVerification); - - ~WebstoreStartupInstaller() override; - - // Implementations of WebstoreStandaloneInstaller Template Method's hooks. - std::unique_ptr<ExtensionInstallPrompt::Prompt> CreateInstallPrompt() - const override; - - private: - bool show_prompt_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreStartupInstaller); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_STARTUP_INSTALLER_H_
diff --git a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc deleted file mode 100644 index 498ab5c..0000000 --- a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc +++ /dev/null
@@ -1,247 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <utility> - -#include "base/command_line.h" -#include "base/scoped_observer.h" -#include "build/build_config.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/webstore_installer_test.h" -#include "chrome/browser/infobars/infobar_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/browser_test_utils.h" -#include "extensions/browser/extension_host.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" -#include "extensions/browser/install/extension_install_ui.h" -#include "extensions/common/extension_builder.h" -#include "extensions/common/value_builder.h" -#include "net/dns/mock_host_resolver.h" -#include "url/gurl.h" - -#if BUILDFLAG(ENABLE_SUPERVISED_USERS) -#include "chrome/browser/supervised_user/supervised_user_constants.h" -#endif - -#if defined(OS_CHROMEOS) -#include "chromeos/chromeos_switches.h" -#endif - -using content::WebContents; -using extensions::DictionaryBuilder; -using extensions::Extension; -using extensions::ExtensionBuilder; -using extensions::ListBuilder; - -const char kWebstoreDomain[] = "cws.com"; -const char kAppDomain[] = "app.com"; -const char kNonAppDomain[] = "nonapp.com"; -const char kTestExtensionId[] = "ecglahbcnmdpdciemllbhojghbkagdje"; -const char kTestDataPath[] = "extensions/api_test/webstore_inline_install"; -const char kCrxFilename[] = "extension.crx"; - -class WebstoreStartupInstallerTest : public WebstoreInstallerTest { - public: - WebstoreStartupInstallerTest() - : WebstoreInstallerTest( - kWebstoreDomain, - kTestDataPath, - kCrxFilename, - kAppDomain, - kNonAppDomain) {} -}; - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, Install) { - AutoAcceptInstall(); - - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install.html")); - - RunTest("runTest"); - - extensions::ExtensionRegistry* registry = - extensions::ExtensionRegistry::Get(browser()->profile()); - const extensions::Extension* extension = - registry->enabled_extensions().GetByID(kTestExtensionId); - EXPECT_TRUE(extension); -} - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, - InstallNotAllowedFromNonVerifiedDomains) { - AutoCancelInstall(); - ui_test_utils::NavigateToURL( - browser(), - GenerateTestServerUrl(kNonAppDomain, "install_non_verified_domain.html")); - - RunTest("runTest1"); - RunTest("runTest2"); -} - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, FindLink) { - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "find_link.html")); - - RunTest("runTest"); -} - -// Flakes on all platforms: http://crbug.com/95713, http://crbug.com/229947 -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, - DISABLED_ArgumentValidation) { - AutoCancelInstall(); - - // Each of these tests has to run separately, since one page/tab can - // only have one in-progress install request. These tests don't all pass - // callbacks to install, so they have no way to wait for the installation - // to complete before starting the next test. - bool is_finished = false; - for (int i = 0; !is_finished; ++i) { - ui_test_utils::NavigateToURL( - browser(), - GenerateTestServerUrl(kAppDomain, "argument_validation.html")); - is_finished = !RunIndexedTest("runTest", i); - } -} - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, MultipleInstallCalls) { - AutoCancelInstall(); - - ui_test_utils::NavigateToURL( - browser(), - GenerateTestServerUrl(kAppDomain, "multiple_install_calls.html")); - RunTest("runTest"); -} - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, InstallNotSupported) { - AutoCancelInstall(); - ui_test_utils::NavigateToURL( - browser(), - GenerateTestServerUrl(kAppDomain, "install_not_supported.html")); - - ui_test_utils::WindowedTabAddedNotificationObserver observer( - content::NotificationService::AllSources()); - RunTest("runTest"); - observer.Wait(); - - // The inline install should fail, and a store-provided URL should be opened - // in a new tab. - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(GURL("http://cws.com/show-me-the-money"), web_contents->GetURL()); -} - -// Regression test for http://crbug.com/144991. -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerTest, InstallFromHostedApp) { - AutoAcceptInstall(); - - const GURL kInstallUrl = GenerateTestServerUrl(kAppDomain, "install.html"); - - // We're forced to construct a hosted app dynamically because we need the - // app to run on a declared URL, but we don't know the port ahead of time. - scoped_refptr<const Extension> hosted_app = - ExtensionBuilder() - .SetManifest( - DictionaryBuilder() - .Set("name", "hosted app") - .Set("version", "1") - .Set( - "app", - DictionaryBuilder() - .Set("urls", - ListBuilder().Append(kInstallUrl.spec()).Build()) - .Set("launch", DictionaryBuilder() - .Set("web_url", kInstallUrl.spec()) - .Build()) - .Build()) - .Set("manifest_version", 2) - .Build()) - .Build(); - ASSERT_TRUE(hosted_app.get()); - - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(browser()->profile()) - ->extension_service(); - extensions::ExtensionRegistry* registry = - extensions::ExtensionRegistry::Get(browser()->profile()); - - extension_service->AddExtension(hosted_app.get()); - EXPECT_TRUE(registry->enabled_extensions().GetByID(hosted_app->id())); - - ui_test_utils::NavigateToURL(browser(), kInstallUrl); - - EXPECT_FALSE(registry->enabled_extensions().GetByID(kTestExtensionId)); - RunTest("runTest"); - EXPECT_TRUE(registry->enabled_extensions().GetByID(kTestExtensionId)); -} - -#if BUILDFLAG(ENABLE_SUPERVISED_USERS) -class WebstoreStartupInstallerSupervisedUsersTest - : public WebstoreStartupInstallerTest { - public: - // InProcessBrowserTest overrides: - void SetUpCommandLine(base::CommandLine* command_line) override { - WebstoreStartupInstallerTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII(switches::kSupervisedUserId, - supervised_users::kChildAccountSUID); -#if defined(OS_CHROMEOS) - command_line->AppendSwitchASCII( - chromeos::switches::kLoginUser, - "supervised_user@locally-managed.localhost"); - command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "hash"); -#endif - } -}; - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallerSupervisedUsersTest, - InstallProhibited) { - AutoAcceptInstall(); - - ui_test_utils::NavigateToURL( - browser(), GenerateTestServerUrl(kAppDomain, "install_prohibited.html")); - - RunTest("runTest"); - - // No error infobar should show up. - WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - InfoBarService* infobar_service = InfoBarService::FromWebContents(contents); - EXPECT_EQ(0u, infobar_service->infobar_count()); -} -#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) - -// The unpack failure test needs to use a different install .crx, which is -// specified via a command-line flag, so it needs its own test subclass. -class WebstoreStartupInstallUnpackFailureTest - : public WebstoreStartupInstallerTest { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - WebstoreStartupInstallerTest::SetUpCommandLine(command_line); - - GURL crx_url = GenerateTestServerUrl( - kWebstoreDomain, "malformed_extension.crx"); - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kAppsGalleryUpdateURL, crx_url.spec()); - } - - void SetUpInProcessBrowserTestFixture() override { - WebstoreStartupInstallerTest::SetUpInProcessBrowserTestFixture(); - extensions::ExtensionInstallUI::set_disable_ui_for_tests(); - } -}; - -IN_PROC_BROWSER_TEST_F(WebstoreStartupInstallUnpackFailureTest, - WebstoreStartupInstallUnpackFailureTest) { - AutoAcceptInstall(); - - ui_test_utils::NavigateToURL(browser(), - GenerateTestServerUrl(kAppDomain, "install_unpack_failure.html")); - - RunTest("runTest"); -}
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc index dc25f92..8c94cdf 100644 --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -700,8 +700,15 @@ if (data_reduction_proxy_io_data && previews_decider_impl) { previews::PreviewsUserData::Create(url_request, previews_decider_impl->GeneratePageId()); - if (data_reduction_proxy_io_data->ShouldAcceptServerPreview( - *url_request, previews_decider_impl)) { + if (url_request->url().SchemeIsHTTPOrHTTPS() && + previews_decider_impl->ShouldAllowPreviewAtECT( + *url_request, previews::PreviewsType::LITE_PAGE, + net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>(), + true) && + previews_decider_impl->ShouldAllowPreviewAtECT( + *url_request, previews::PreviewsType::LOFI, + net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>(), + true)) { previews_state |= content::SERVER_LOFI_ON; previews_state |= content::SERVER_LITE_PAGE_ON; }
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc index 9e4abd92..af8bb6d 100644 --- a/chrome/browser/media/encrypted_media_browsertest.cc +++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -299,9 +299,7 @@ } void SetUpCommandLine(base::CommandLine* command_line) override { - command_line->AppendSwitchASCII( - switches::kAutoplayPolicy, - switches::autoplay::kNoUserGestureRequiredPolicy); + MediaBrowserTest::SetUpCommandLine(command_line); command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, "EncryptedMediaHdcpPolicyCheck"); } @@ -317,11 +315,12 @@ void SetUpCommandLineForKeySystem(const std::string& key_system, base::CommandLine* command_line) { - if (GetServerConfig(key_system)) + if (GetServerConfig(key_system)) { // Since the web and license servers listen on different ports, we need to // disable web-security to send license requests to the license server. // TODO(shadi): Add port forwarding to the test web server configuration. command_line->AppendSwitch(switches::kDisableWebSecurity); + } #if BUILDFLAG(ENABLE_LIBRARY_CDMS) if (IsExternalClearKey(key_system)) {
diff --git a/chrome/browser/media/media_browsertest.cc b/chrome/browser/media/media_browsertest.cc index cbc9e42..47a2d406 100644 --- a/chrome/browser/media/media_browsertest.cc +++ b/chrome/browser/media/media_browsertest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/media/media_browsertest.h" +#include "base/command_line.h" #include "base/i18n/time_formatting.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -14,6 +15,7 @@ #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" +#include "media/base/media_switches.h" #include "media/base/test_data_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -21,6 +23,15 @@ MediaBrowserTest::~MediaBrowserTest() {} +void MediaBrowserTest::SetUpCommandLine(base::CommandLine* command_line) { + command_line->AppendSwitchASCII( + switches::kAutoplayPolicy, + switches::autoplay::kNoUserGestureRequiredPolicy); + // Disable fallback after decode error to avoid unexpected test pass on the + // fallback path. + scoped_feature_list_.InitAndDisableFeature(media::kFallbackAfterDecodeError); +} + void MediaBrowserTest::RunMediaTestPage(const std::string& html_page, const base::StringPairs& query_params, const std::string& expected_title,
diff --git a/chrome/browser/media/media_browsertest.h b/chrome/browser/media/media_browsertest.h index 9c50ba2..1ecc195 100644 --- a/chrome/browser/media/media_browsertest.h +++ b/chrome/browser/media/media_browsertest.h
@@ -7,6 +7,7 @@ #include <string> +#include "base/test/scoped_feature_list.h" #include "chrome/test/base/in_process_browser_test.h" #include "media/base/test_data_util.h" @@ -22,6 +23,9 @@ MediaBrowserTest(); ~MediaBrowserTest() override; + // InProcessBrowserTest implementation. + void SetUpCommandLine(base::CommandLine* command_line) override; + // Runs a html page with a list of URL query parameters. // If http is true, the test starts a local http test server to load the test // page, otherwise a local file URL is loaded inside the content shell. @@ -36,6 +40,9 @@ std::string RunTest(const GURL& gurl, const std::string& expected); virtual void AddWaitForTitles(content::TitleWatcher* title_watcher); + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; #endif // CHROME_BROWSER_MEDIA_MEDIA_BROWSERTEST_H_
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc index 82e1225..ff73cb7 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.cc
@@ -17,6 +17,8 @@ #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" +#include "chrome/browser/previews/previews_service.h" +#include "chrome/browser/previews/previews_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/common/pref_names.h" @@ -30,6 +32,7 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" +#include "components/previews/content/previews_ui_service.h" #include "components/proxy_config/proxy_config_pref_names.h" #include "components/proxy_config/proxy_prefs.h" #include "content/public/browser/browser_thread.h" @@ -175,8 +178,8 @@ DataReductionProxyChromeSettings::DataReductionProxyChromeSettings() : data_reduction_proxy::DataReductionProxySettings(), - data_reduction_proxy_enabled_pref_name_(prefs::kDataSaverEnabled) { -} + data_reduction_proxy_enabled_pref_name_(prefs::kDataSaverEnabled), + profile_(nullptr) {} DataReductionProxyChromeSettings::~DataReductionProxyChromeSettings() { } @@ -192,10 +195,12 @@ data_reduction_proxy::DataReductionProxyIOData* io_data, PrefService* profile_prefs, net::URLRequestContextGetter* request_context_getter, + Profile* profile, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, std::unique_ptr<data_reduction_proxy::DataStore> store, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, const scoped_refptr<base::SequencedTaskRunner>& db_task_runner) { + profile_ = profile; DCHECK_CURRENTLY_ON(content::BrowserThread::UI); #if defined(OS_ANDROID) // On mobile we write Data Reduction Proxy prefs directly to the pref service. @@ -231,6 +236,18 @@ MigrateDataReductionProxyOffProxyPrefs(profile_prefs); } +void DataReductionProxyChromeSettings::SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) { + // |previews_service| is null if |profile_| is off the record. + PreviewsService* previews_service = + PreviewsServiceFactory::GetForProfile(profile_); + if (previews_service && previews_service->previews_ui_service()) { + previews_service->previews_ui_service() + ->SetIgnoreLongTermBlackListForServerPreviews( + ignore_long_term_black_list_rules); + } +} + // static data_reduction_proxy::Client DataReductionProxyChromeSettings::GetClient() { #if defined(OS_ANDROID)
diff --git a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h index 49189b5..2fa3d69 100644 --- a/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h +++ b/chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h
@@ -14,6 +14,7 @@ #include "components/keyed_service/core/keyed_service.h" class PrefService; +class Profile; namespace base { class SequencedTaskRunner; @@ -72,6 +73,7 @@ data_reduction_proxy::DataReductionProxyIOData* io_data, PrefService* profile_prefs, net::URLRequestContextGetter* request_context_getter, + Profile* profile, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, std::unique_ptr<data_reduction_proxy::DataStore> store, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, @@ -90,6 +92,9 @@ data_reduction_proxy_enabled_pref_name_ = pref_name; } + void SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) override; + private: // Helper method for migrating the Data Reduction Proxy away from using the // proxy pref. Returns the ProxyPrefMigrationResult value indicating the @@ -99,6 +104,9 @@ std::string data_reduction_proxy_enabled_pref_name_; + // Null before InitDataReductionProxySettings is called. + Profile* profile_; + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyChromeSettings); };
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc index 65ddccc..bc645c4 100644 --- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -243,5 +243,8 @@ } // page_transition_ fits in a uint32_t, so we can safely cast to int64_t. builder.SetNavigation_PageTransition(static_cast<int64_t>(page_transition_)); + // info.page_end_reason fits in a uint32_t, so we can safely cast to int64_t. + builder.SetNavigation_PageEndReason( + static_cast<int64_t>(info.page_end_reason)); builder.Record(ukm::UkmRecorder::Get()); }
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc index abdccd4..a60b4096 100644 --- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc +++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -123,6 +123,9 @@ kv.second.get(), PageLoad::kNavigation_PageTransitionName, ui::PAGE_TRANSITION_LINK); test_ukm_recorder().ExpectEntryMetric( + kv.second.get(), PageLoad::kNavigation_PageEndReasonName, + page_load_metrics::END_CLOSE); + test_ukm_recorder().ExpectEntryMetric( kv.second.get(), PageLoad::kParseTiming_NavigationToParseStartName, 100); test_ukm_recorder().ExpectEntryMetric( @@ -178,6 +181,9 @@ kv.second.get(), PageLoad::kNavigation_PageTransitionName, ui::PAGE_TRANSITION_LINK); test_ukm_recorder().ExpectEntryMetric( + kv.second.get(), PageLoad::kNavigation_PageEndReasonName, + page_load_metrics::END_PROVISIONAL_LOAD_FAILED); + test_ukm_recorder().ExpectEntryMetric( kv.second.get(), PageLoad::kNet_EffectiveConnectionType2_OnNavigationStartName, metrics::SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G); @@ -386,6 +392,9 @@ ASSERT_NE(entry2, nullptr); test_ukm_recorder().ExpectEntrySourceHasUrl(entry1, GURL(kTestUrl1)); + test_ukm_recorder().ExpectEntryMetric(entry1, + PageLoad::kNavigation_PageEndReasonName, + page_load_metrics::END_NEW_NAVIGATION); test_ukm_recorder().ExpectEntryMetric( entry1, PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName, 200); EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( @@ -396,6 +405,9 @@ entry1, PageLoad::kPageTiming_ForegroundDurationName)); test_ukm_recorder().ExpectEntrySourceHasUrl(entry2, GURL(kTestUrl2)); + test_ukm_recorder().ExpectEntryMetric(entry2, + PageLoad::kNavigation_PageEndReasonName, + page_load_metrics::END_CLOSE); EXPECT_FALSE(test_ukm_recorder().EntryHasMetric( entry2, PageLoad::kParseTiming_NavigationToParseStartName)); EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
diff --git a/chrome/browser/previews/previews_infobar_delegate_unittest.cc b/chrome/browser/previews/previews_infobar_delegate_unittest.cc index 36b760f..013b8b5 100644 --- a/chrome/browser/previews/previews_infobar_delegate_unittest.cc +++ b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
@@ -35,6 +35,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config_test_utils.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h" @@ -185,7 +186,7 @@ drp_test_context_->GetDataReductionProxyEnabledPrefName()); data_reduction_proxy_settings->InitDataReductionProxySettings( drp_test_context_->io_data(), drp_test_context_->pref_service(), - drp_test_context_->request_context_getter(), + drp_test_context_->request_context_getter(), profile(), base::MakeRefCounted<network::TestSharedURLLoaderFactory>(), base::WrapUnique(new data_reduction_proxy::DataStore()), base::ThreadTaskRunnerHandle::Get(),
diff --git a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc index a82e3f54e..cc2ef9dff 100644 --- a/chrome/browser/previews/previews_ui_tab_helper_unittest.cc +++ b/chrome/browser/previews/previews_ui_tab_helper_unittest.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h" #include "chrome/browser/previews/previews_ui_tab_helper.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "chrome/test/base/testing_profile.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_compression_stats.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h" #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h" @@ -80,7 +81,7 @@ drp_test_context_->GetDataReductionProxyEnabledPrefName()); data_reduction_proxy_settings->InitDataReductionProxySettings( drp_test_context_->io_data(), drp_test_context_->pref_service(), - drp_test_context_->request_context_getter(), + drp_test_context_->request_context_getter(), profile(), base::MakeRefCounted<network::TestSharedURLLoaderFactory>(), base::WrapUnique(new data_reduction_proxy::DataStore()), base::ThreadTaskRunnerHandle::Get(),
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc index 790785ed..dede943a 100644 --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -244,6 +244,7 @@ base::FeatureList::IsEnabled(network::features::kNetworkService) ? nullptr : main_request_context_getter_.get(), + profile_, content::BrowserContext::GetDefaultStoragePartition(profile_) ->GetURLLoaderFactoryForBrowserProcess(), std::move(store),
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc index 449100e3..4f73e9d0 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -439,7 +439,7 @@ drp_test_context_->GetDataReductionProxyEnabledPrefName()); settings->InitDataReductionProxySettings( drp_test_context_->io_data(), drp_test_context_->pref_service(), - drp_test_context_->request_context_getter(), + drp_test_context_->request_context_getter(), profile(), base::MakeRefCounted<network::TestSharedURLLoaderFactory>(), std::make_unique<data_reduction_proxy::DataStore>(), base::ThreadTaskRunnerHandle::Get(),
diff --git a/chrome/browser/resources/chromeos/switch_access/automation_manager.js b/chrome/browser/resources/chromeos/switch_access/automation_manager.js index dff1ccd..9f2238cd 100644 --- a/chrome/browser/resources/chromeos/switch_access/automation_manager.js +++ b/chrome/browser/resources/chromeos/switch_access/automation_manager.js
@@ -251,8 +251,9 @@ getRelevantMenuActions_() { // TODO(crbug/881080): determine relevant actions programmatically. let actions = [ - ContextMenuManager.Action.CLICK, ContextMenuManager.Action.OPTIONS, - ContextMenuManager.Action.SCROLL_UP, ContextMenuManager.Action.SCROLL_DOWN + ContextMenuManager.Action.CLICK, ContextMenuManager.Action.DICTATION, + ContextMenuManager.Action.OPTIONS, ContextMenuManager.Action.SCROLL_UP, + ContextMenuManager.Action.SCROLL_DOWN ]; return actions; }
diff --git a/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js b/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js index ae8e4b6e..476569a 100644 --- a/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js +++ b/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js
@@ -194,6 +194,8 @@ if (event.data === ContextMenuManager.Action.CLICK) this.automationManager_.selectCurrentNode(); + else if (event.data === ContextMenuManager.Action.DICTATION) + chrome.accessibilityPrivate.toggleDictation(); else if (event.data === ContextMenuManager.Action.OPTIONS) window.switchAccess.showOptionsPage(); else if ( @@ -228,6 +230,7 @@ */ ContextMenuManager.Action = { CLICK: 'click', + DICTATION: 'dictation', OPTIONS: 'options', SCROLL_BACKWARD: 'scroll-backward', SCROLL_DOWN: 'scroll-down',
diff --git a/chrome/browser/resources/chromeos/switch_access/panel.html b/chrome/browser/resources/chromeos/switch_access/panel.html index c1b834a..57d062f 100644 --- a/chrome/browser/resources/chromeos/switch_access/panel.html +++ b/chrome/browser/resources/chromeos/switch_access/panel.html
@@ -19,6 +19,7 @@ <button class="action" id="scroll-up" disabled>Scroll up</button> <button class="action" id="scroll-right" disabled>Scroll right</button> <button class="action" id="scroll-left" disabled>Scroll left</button> + <button class="action" id="dictation">Dictation</button> <button class="action" id="options">Options</button> </div> </body>
diff --git a/chrome/browser/resources/md_extensions/options_dialog.js b/chrome/browser/resources/md_extensions/options_dialog.js index 817862f..29f082c6 100644 --- a/chrome/browser/resources/md_extensions/options_dialog.js +++ b/chrome/browser/resources/md_extensions/options_dialog.js
@@ -84,7 +84,13 @@ preferredSize = e; if (!this.$.dialog.open) this.$.dialog.showModal(); - this.updateDialogSize_(preferredSize.width, preferredSize.height); + // Updating the dialog size can result in a preferred size change, so + // wait until request animation frame fires before updating the dialog + // size. This hysteresis prevents the preferred size from oscillating + // (see: https://crbug.com/882835). + requestAnimationFrame(() => { + this.updateDialogSize_(preferredSize.width, preferredSize.height); + }); }; this.boundResizeListener_ = () => {
diff --git a/chrome/browser/resources/ntp4/new_tab.js b/chrome/browser/resources/ntp4/new_tab.js index 98066bfe..f47884a 100644 --- a/chrome/browser/resources/ntp4/new_tab.js +++ b/chrome/browser/resources/ntp4/new_tab.js
@@ -221,14 +221,16 @@ */ function layoutFooter() { // We need the image to be loaded. - var logo = $('logo-img'); - var logoImg = logo.querySelector('img'); - if (!logoImg.complete) { + let logo = $('logo-img'); + let logoImg = logo.querySelector('img'); + + // Only compare the width after the footer image successfully loaded. + if (!logoImg.complete || logoImg.width === 0) { logoImg.onload = layoutFooter; return; } - var menu = $('footer-menu-container'); + let menu = $('footer-menu-container'); if (menu.clientWidth > logoImg.width) logo.style.WebkitFlex = '0 1 ' + menu.clientWidth + 'px'; else
diff --git a/chrome/browser/resources/print_preview/new/advanced_options_settings.html b/chrome/browser/resources/print_preview/new/advanced_options_settings.html index 8165993..a7f252c 100644 --- a/chrome/browser/resources/print_preview/new/advanced_options_settings.html +++ b/chrome/browser/resources/print_preview/new/advanced_options_settings.html
@@ -22,7 +22,7 @@ } </style> <print-preview-settings-section> - <span slot="title">$i18n{advancedOptionsLabel}</span> + <span slot="title"></span> <div slot="controls"> <paper-button id="button" disabled$="[[disabled]]" on-click="onButtonClick_">
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html index 8236c723c..c89d234 100644 --- a/chrome/browser/resources/print_preview/new/app.html +++ b/chrome/browser/resources/print_preview/new/app.html
@@ -69,20 +69,12 @@ padding-bottom: 16px; } - #container > *, - #container iron-collapse > * { + .settings-section { display: block; margin-bottom: 16px; margin-top: 16px; } - #container print-preview-more-settings, - #container iron-collapse, - #container print-preview-link-container { - margin-bottom: 0; - margin-top: 0; - } - #preview-area-container { align-items: center; background-color: var(--google-grey-200); @@ -108,24 +100,24 @@ invitation-store="[[invitationStore_]]" disabled="[[controlsDisabled_]]" state="[[state]]" recent-destinations="[[recentDestinations_]]" - user-info="{{userInfo_}}" available> + user-info="{{userInfo_}}" available class="settings-section"> </print-preview-destination-settings> <print-preview-pages-settings settings="{{settings}}" document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.pages.available]]"> + hidden$="[[!settings.pages.available]]" class="settings-section"> </print-preview-pages-settings> <print-preview-copies-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.copies.available]]"> + hidden$="[[!settings.copies.available]]" class="settings-section"> </print-preview-copies-settings> <print-preview-layout-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.layout.available]]"> + hidden$="[[!settings.layout.available]]" class="settings-section"> </print-preview-layout-settings> <print-preview-color-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.color.available]]"> + hidden$="[[!settings.color.available]]" class="settings-section"> </print-preview-color-settings> <print-preview-more-settings settings-expanded-by-user="{{settingsExpandedByUser_}}" @@ -138,35 +130,41 @@ <print-preview-media-size-settings settings="{{settings}}" capability="[[destination_.capabilities.printer.media_size]]" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.mediaSize.available]]"> + hidden$="[[!settings.mediaSize.available]]" + class="settings-section"> </print-preview-media-size-settings> <template is="dom-if" if="[[showPagesPerSheet_]]"> <print-preview-pages-per-sheet-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.pagesPerSheet.available]]"> + hidden$="[[!settings.pagesPerSheet.available]]" + class="settings-section"> </print-preview-pages-per-sheet-settings> </template> <print-preview-margins-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.margins.available]]"> + hidden$="[[!settings.margins.available]]" + class="settings-section"> </print-preview-margins-settings> <print-preview-dpi-settings settings="{{settings}}" capability="[[destination_.capabilities.printer.dpi]]" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.dpi.available]]"> + hidden$="[[!settings.dpi.available]]" class="settings-section"> </print-preview-dpi-settings> <print-preview-scaling-settings settings="{{settings}}" document-info="[[documentInfo_]]" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.scaling.available]]"> + hidden$="[[!settings.scaling.available]]" + class="settings-section"> </print-preview-scaling-settings> <print-preview-other-options-settings settings="{{settings}}" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.otherOptions.available]]"> + hidden$="[[!settings.otherOptions.available]]" + class="settings-section"> </print-preview-other-options-settings> <print-preview-advanced-options-settings settings="{{settings}}" destination="[[destination_]]" disabled="[[controlsDisabled_]]" - hidden$="[[!settings.vendorItems.available]]"> + hidden$="[[!settings.vendorItems.available]]" + class="settings-section"> </print-preview-advanced-options-settings> </iron-collapse> <if expr="not chromeos">
diff --git a/chrome/browser/resources/print_preview/new/destination_dialog.html b/chrome/browser/resources/print_preview/new/destination_dialog.html index 93933594..71de939 100644 --- a/chrome/browser/resources/print_preview/new/destination_dialog.html +++ b/chrome/browser/resources/print_preview/new/destination_dialog.html
@@ -74,16 +74,20 @@ flex-direction: column; } - /* Height = 3 * destination item (28px) + 10px padding + 1 line text */ + /* Height = 3 * destination item + 10px padding + 1 line text */ + #recentList, + #printList { + min-height: + calc(3 * var(--destination-item-height) + 10px + 20 / 13 * 1rem); + } + #recentList { flex: 0; - min-height: calc(94px + 20 / 13 * 1rem); padding-bottom: 18px; } #printList { flex: 1; - min-height: calc(94px + 20 / 13 * 1rem); padding-bottom: 0; }
diff --git a/chrome/browser/resources/print_preview/new/destination_list.html b/chrome/browser/resources/print_preview/new/destination_list.html index 7a989b3..f86f651 100644 --- a/chrome/browser/resources/print_preview/new/destination_list.html +++ b/chrome/browser/resources/print_preview/new/destination_list.html
@@ -26,7 +26,7 @@ #listContainer { flex: 1; - min-height: 84px; + min-height: calc(3 * var(--destination-item-height)); } .title,
diff --git a/chrome/browser/resources/print_preview/new/destination_list.js b/chrome/browser/resources/print_preview/new/destination_list.js index 16f8ab4..35effe3 100644 --- a/chrome/browser/resources/print_preview/new/destination_list.js +++ b/chrome/browser/resources/print_preview/new/destination_list.js
@@ -65,7 +65,7 @@ const entry = assert(entries[0]); // Don't set maxHeight below the minimum height. - const fullHeight = Math.max(entry.contentRect.height, 84); + const fullHeight = Math.max(entry.contentRect.height, 96); this.$.list.style.maxHeight = `${fullHeight}px`; this.forceIronResize(); });
diff --git a/chrome/browser/resources/print_preview/new/destination_list_item.html b/chrome/browser/resources/print_preview/new/destination_list_item.html index 0c1f640..294788b1 100644 --- a/chrome/browser/resources/print_preview/new/destination_list_item.html +++ b/chrome/browser/resources/print_preview/new/destination_list_item.html
@@ -20,7 +20,7 @@ cursor: default; display: flex; font-size: calc(12/13 * 1em); - min-height: 28px; + min-height: var(--destination-item-height); opacity: .87; padding-bottom: 2px; padding-inline-end: 2px;
diff --git a/chrome/browser/resources/print_preview/new/destination_settings.html b/chrome/browser/resources/print_preview/new/destination_settings.html index 7d4c271..7b5da3c2 100644 --- a/chrome/browser/resources/print_preview/new/destination_settings.html +++ b/chrome/browser/resources/print_preview/new/destination_settings.html
@@ -22,13 +22,9 @@ <dom-module id="print-preview-destination-settings"> <template> <style include="print-preview-shared paper-button-style throbber cr-hidden-style"> - print-preview-settings-section { - margin-top: 16px; - } - paper-button { margin: 2px; - margin-top: 12px; + margin-top: 8px; width: 89px; } @@ -40,7 +36,6 @@ .throbber-container { align-items: center; display: flex; - min-height: 28px; overflow: hidden; } @@ -56,16 +51,16 @@ overflow: hidden; } + .destination-name { + line-height: calc(20/13 * 1em); + } + .destination-location, .destination-connection-status { color: var(--google-grey-700); font-size: calc(12/13 * 1em); } - .destination-name { - margin-bottom: 4px; - } - .destination-info-wrapper > div, .destination-throbber-name { flex: 1; @@ -78,7 +73,7 @@ opacity: 0.4; } </style> - <print-preview-settings-section class="multirow-controls"> + <print-preview-settings-section> <span slot="title">$i18n{destinationLabel}</span> <div slot="controls"> <div class="throbber-container" hidden="[[!loadingDestination_]]"> @@ -96,6 +91,11 @@ </div> </div> </div> + </div> + </print-preview-settings-section> + <print-preview-settings-section> + <div slot="title"></div> + <div slot="controls"> <paper-button disabled$="[[shouldDisableButton_(destinationStore, disabled, state)]]"
diff --git a/chrome/browser/resources/print_preview/new/link_container.html b/chrome/browser/resources/print_preview/new/link_container.html index db63ae11..061c4f4 100644 --- a/chrome/browser/resources/print_preview/new/link_container.html +++ b/chrome/browser/resources/print_preview/new/link_container.html
@@ -9,6 +9,14 @@ <dom-module id="print-preview-link-container"> <template> <style include="print-preview-shared throbber cr-hidden-style"> + :host paper-icon-button-light { + --cr-paper-icon-button-margin: { + margin-inline-end: -6px; + margin-inline-start: 0; + }; + --cr-icon-size: 16px; + } + .link:not([actionable]) { pointer-events: none; }
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html index 1903cc45..e41aeab9 100644 --- a/chrome/browser/resources/print_preview/new/pages_settings.html +++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -35,10 +35,14 @@ cursor: default; height: 100%; } + + :host #title { + justify-content: flex-start; + padding-top: 9px; + } </style> - <print-preview-settings-section - class="input-settings-section multirow-controls"> - <span slot="title">$i18n{pagesLabel}</span> + <print-preview-settings-section> + <span id="title" slot="title">$i18n{pagesLabel}</span> <div slot="controls" id="controls"> <paper-radio-group selectable="cr-radio-button" class="radio" disabled$="[[controlsDisabled_]]"
diff --git a/chrome/browser/resources/print_preview/new/print_preview_shared_css.html b/chrome/browser/resources/print_preview/new/print_preview_shared_css.html index 44495d0..a1d893cc 100644 --- a/chrome/browser/resources/print_preview/new/print_preview_shared_css.html +++ b/chrome/browser/resources/print_preview/new/print_preview_shared_css.html
@@ -17,6 +17,7 @@ --print-preview-dialog-margin: 34px; --cr-form-field-label-height: 1.5rem; --cr-form-field-label-line-height: .75rem; + --destination-item-height: 32px; } /* Default state ********************************************************/
diff --git a/chrome/browser/resources/print_preview/new/settings_section.html b/chrome/browser/resources/print_preview/new/settings_section.html index 475ce1b..a01102f 100644 --- a/chrome/browser/resources/print_preview/new/settings_section.html +++ b/chrome/browser/resources/print_preview/new/settings_section.html
@@ -34,16 +34,12 @@ color: var(--google-grey-900); flex: none; font-size: 1em; + line-height: calc(20/13 * 1em); padding-inline-end: var(--policy-icon-padding); width: calc(75px - var(--policy-icon-padding)); word-break: break-word; } - :host(.multirow-controls) ::slotted([slot=title]) { - justify-content: flex-start; - padding-top: 10px; - } - :host([managed]:not([show-policy-on-end])) ::slotted([slot=title]) { width: calc(75px - var(--policy-icon-size) - var(--policy-icon-padding));
diff --git a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc index d6215cc..e44c4264 100644 --- a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc +++ b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.cc
@@ -452,10 +452,8 @@ // This proxy should only be installed for subresource requests from a frame // that is rendering the GAIA signon realm. - if (!render_frame_host || - !gaia::IsGaiaSignonRealm(request_initiator.GetURL())) { + if (!gaia::IsGaiaSignonRealm(request_initiator.GetURL())) return false; - } auto* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 8d42b00..a9bbebb 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1400,8 +1400,6 @@ "ash/ash_shell_init.h", "ash/ash_util.cc", "ash/ash_util.h", - "ash/browser_image_registrar.cc", - "ash/browser_image_registrar.h", "ash/cast_config_client_media_router.cc", "ash/cast_config_client_media_router.h", "ash/chrome_accessibility_delegate.cc",
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc index 599d0477..5c30dfd 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.cc +++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -396,7 +396,7 @@ apps_builder_ = std::make_unique<ExtensionAppModelBuilder>(controller); if (arc::IsArcAllowedForProfile(profile_)) arc_apps_builder_ = std::make_unique<ArcAppModelBuilder>(controller); - if (IsCrostiniUIAllowedForProfile(profile_)) { + if (crostini::IsCrostiniUIAllowedForProfile(profile_)) { crostini_apps_builder_ = std::make_unique<CrostiniAppModelBuilder>(controller); }
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc index 359e479..7f01f005 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_context_menu.cc
@@ -23,7 +23,7 @@ void CrostiniAppContextMenu::BuildMenu(ui::SimpleMenuModel* menu_model) { app_list::AppContextMenu::BuildMenu(menu_model); - if (app_id() == kCrostiniTerminalId) { + if (app_id() == crostini::kCrostiniTerminalId) { if (!features::IsTouchableAppContextMenuEnabled()) menu_model->AddSeparator(ui::NORMAL_SEPARATOR); @@ -36,12 +36,12 @@ bool CrostiniAppContextMenu::IsCommandIdEnabled(int command_id) const { if (command_id == ash::UNINSTALL) { - if (app_id() == kCrostiniTerminalId) { - return IsCrostiniEnabled(profile()); + if (app_id() == crostini::kCrostiniTerminalId) { + return crostini::IsCrostiniEnabled(profile()); } } else if (command_id == ash::MENU_CLOSE) { - if (app_id() == kCrostiniTerminalId) { - return IsCrostiniRunning(profile()); + if (app_id() == crostini::kCrostiniTerminalId) { + return crostini::IsCrostiniRunning(profile()); } } return app_list::AppContextMenu::IsCommandIdEnabled(command_id); @@ -50,16 +50,17 @@ void CrostiniAppContextMenu::ExecuteCommand(int command_id, int event_flags) { switch (command_id) { case ash::UNINSTALL: - if (app_id() == kCrostiniTerminalId) { - ShowCrostiniUninstallerView(profile(), CrostiniUISurface::kAppList); + if (app_id() == crostini::kCrostiniTerminalId) { + crostini::ShowCrostiniUninstallerView( + profile(), crostini::CrostiniUISurface::kAppList); return; } break; case ash::MENU_CLOSE: - if (app_id() == kCrostiniTerminalId) { + if (app_id() == crostini::kCrostiniTerminalId) { crostini::CrostiniManager::GetForProfile(profile())->StopVm( - kCrostiniDefaultVmName, base::DoNothing()); + crostini::kCrostiniDefaultVmName, base::DoNothing()); return; } break;
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc b/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc index e2d09411..fc7e1d87 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_icon.cc
@@ -100,7 +100,7 @@ // Host loads icon asynchronously, so use default icon so far. int resource_id; - if (host_ && host_->app_id() == kCrostiniTerminalId) { + if (host_ && host_->app_id() == crostini::kCrostiniTerminalId) { // Don't initiate the icon request from the container because we have this // one already. resource_id = IDR_LOGO_CROSTINI_TERMINAL;
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc index 972fda1..96441c5 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_item.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_item.cc
@@ -38,7 +38,7 @@ // Crostini app is created from scratch. Move it to default folder. DCHECK(folder_id().empty()); - SetChromeFolderId(kCrostiniFolderId); + SetChromeFolderId(crostini::kCrostiniFolderId); } // Set model updater last to avoid being called during construction.
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc index 1999fed..2f982a4 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc
@@ -44,7 +44,8 @@ void CrostiniAppModelBuilder::InsertCrostiniAppItem( const crostini::CrostiniRegistryService* registry_service, const std::string& app_id) { - if (app_id == kCrostiniTerminalId && !IsCrostiniEnabled(profile())) { + if (app_id == crostini::kCrostiniTerminalId && + !crostini::IsCrostiniEnabled(profile())) { // If Crostini isn't enabled, don't show the Terminal item until it // becomes enabled. return; @@ -107,15 +108,15 @@ void CrostiniAppModelBuilder::OnCrostiniEnabledChanged() { const bool unsynced_change = false; - if (IsCrostiniEnabled(profile())) { + if (crostini::IsCrostiniEnabled(profile())) { // If Terminal has been installed before and has not been cleaned up // correctly, it needs to be removed. - RemoveApp(kCrostiniTerminalId, unsynced_change); + RemoveApp(crostini::kCrostiniTerminalId, unsynced_change); crostini::CrostiniRegistryService* registry_service = crostini::CrostiniRegistryServiceFactory::GetForProfile(profile()); - InsertCrostiniAppItem(registry_service, kCrostiniTerminalId); + InsertCrostiniAppItem(registry_service, crostini::kCrostiniTerminalId); } else { - RemoveApp(kCrostiniTerminalId, unsynced_change); + RemoveApp(crostini::kCrostiniTerminalId, unsynced_change); } } @@ -125,13 +126,13 @@ root_folder_created_ = true; const app_list::AppListSyncableService::SyncItem* sync_item = - GetSyncItem(kCrostiniFolderId); + GetSyncItem(crostini::kCrostiniFolderId); if (sync_item) return; std::unique_ptr<ChromeAppListItem> crositini_folder = - std::make_unique<ChromeAppListItem>(profile(), kCrostiniFolderId, - model_updater()); + std::make_unique<ChromeAppListItem>( + profile(), crostini::kCrostiniFolderId, model_updater()); crositini_folder->SetChromeIsFolder(true); crositini_folder->SetName( l10n_util::GetStringUTF8(IDS_APP_LIST_CROSTINI_DEFAULT_FOLDER_NAME));
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc index 9f8606d8..9a6b96d 100644 --- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc +++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
@@ -73,7 +73,7 @@ std::vector<std::string> AppendRootFolderId( const std::vector<std::string> ids) { std::vector<std::string> result = ids; - result.emplace_back(kCrostiniFolderId); + result.emplace_back(crostini::kCrostiniFolderId); return result; } @@ -126,18 +126,18 @@ // Test that the Terminal app is only shown when Crostini is enabled TEST_F(CrostiniAppModelBuilderTest, EnableCrostini) { - SetCrostiniUIAllowedForTesting(true); + crostini::SetCrostiniUIAllowedForTesting(true); EXPECT_EQ(0u, model_updater_->ItemCount()); CrostiniTestHelper::EnableCrostini(profile()); // Root folder + terminal app. - EXPECT_THAT( - GetAppIds(model_updater_.get()), - testing::UnorderedElementsAre(kCrostiniFolderId, kCrostiniTerminalId)); + EXPECT_THAT(GetAppIds(model_updater_.get()), + testing::UnorderedElementsAre(crostini::kCrostiniFolderId, + crostini::kCrostiniTerminalId)); EXPECT_THAT(GetAppNames(model_updater_.get()), testing::UnorderedElementsAre(kRootFolderName, GetFullName(TerminalAppName()))); - SetCrostiniUIAllowedForTesting(false); + crostini::SetCrostiniUIAllowedForTesting(false); } TEST_F(CrostiniAppModelBuilderTest, AppInstallation) { @@ -180,7 +180,7 @@ EXPECT_EQ(3u, model_updater_->ItemCount()); EXPECT_THAT(GetAppIds(model_updater_.get()), testing::UnorderedElementsAre( - kCrostiniFolderId, kCrostiniTerminalId, + crostini::kCrostiniFolderId, crostini::kCrostiniTerminalId, CrostiniTestHelper::GenerateAppId(kDummpyApp2Id))); // Setting NoDisplay to false should unhide an app. @@ -230,8 +230,9 @@ // The uninstall flow removes all apps before setting the CrostiniEnabled pref // to false, so we need to do that explicitly too. - RegistryService()->ClearApplicationList(kCrostiniDefaultVmName, - kCrostiniDefaultContainerName); + RegistryService()->ClearApplicationList( + crostini::kCrostiniDefaultVmName, + crostini::kCrostiniDefaultContainerName); CrostiniTestHelper::DisableCrostini(profile()); // Root folder is left. We rely on default handling of empty folder. EXPECT_EQ(1u, model_updater_->ItemCount());
diff --git a/chrome/browser/ui/app_list/search/app_search_provider.cc b/chrome/browser/ui/app_list/search/app_search_provider.cc index 3dc210f..2cea38b 100644 --- a/chrome/browser/ui/app_list/search/app_search_provider.cc +++ b/chrome/browser/ui/app_list/search/app_search_provider.cc
@@ -475,7 +475,8 @@ // Until it's been installed, the Terminal is hidden unless you search // for 'Terminal' exactly (case insensitive). - if (app_id == kCrostiniTerminalId && !IsCrostiniEnabled(profile())) { + if (app_id == crostini::kCrostiniTerminalId && + !crostini::IsCrostiniEnabled(profile())) { apps->back()->set_recommendable(false); apps->back()->set_require_exact_match(true); } @@ -521,7 +522,7 @@ std::make_unique<ExtensionDataSource>(profile, this)); if (arc::IsArcAllowedForProfile(profile)) data_sources_.emplace_back(std::make_unique<ArcDataSource>(profile, this)); - if (IsCrostiniUIAllowedForProfile(profile)) { + if (crostini::IsCrostiniUIAllowedForProfile(profile)) { data_sources_.emplace_back( std::make_unique<CrostiniDataSource>(profile, this)); }
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc index 6ec094a7..bef3dee6 100644 --- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc +++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
@@ -45,6 +45,8 @@ void SetSelectToSpeakState(ash::mojom::SelectToSpeakState state) override {} void SetSelectToSpeakEventHandlerDelegate( ash::mojom::SelectToSpeakEventHandlerDelegatePtr delegate) override {} + void ToggleDictationFromSource( + ash::mojom::DictationToggleSource source) override {} bool was_client_set() const { return was_client_set_; }
diff --git a/chrome/browser/ui/ash/browser_image_registrar.cc b/chrome/browser/ui/ash/browser_image_registrar.cc deleted file mode 100644 index 5b23fa2..0000000 --- a/chrome/browser/ui/ash/browser_image_registrar.cc +++ /dev/null
@@ -1,148 +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 "chrome/browser/ui/ash/browser_image_registrar.h" - -#include <map> - -#include "ash/public/interfaces/client_image_registry.mojom.h" -#include "ash/public/interfaces/constants.mojom.h" -#include "base/macros.h" -#include "content/public/common/service_manager_connection.h" -#include "services/service_manager/public/cpp/connector.h" - -namespace { - -class BrowserImageRegistrarImpl { - public: - BrowserImageRegistrarImpl(); - ~BrowserImageRegistrarImpl(); - - scoped_refptr<ImageRegistration> RegisterImage(const gfx::ImageSkia& image); - void ForgetImage(const base::UnguessableToken& token); - - const std::map<base::UnguessableToken, const void*>& tokens() const { - return tokens_; - } - const std::map<const void*, ImageRegistration*>& images() const { - return images_; - } - - // Initializes the singleton instance, if necessary. This will only init - // |g_registrar| one time; after that it's a no-op. - static void InitOnce(); - - private: - // The void* in both of the maps is the object that backs the associated - // ImageSkia. This pointer is guaranteed to remain valid as long as an - // ImageSkia references it, which is guaranteed by the existence of the - // ImageRegistration. - std::map<base::UnguessableToken, const void*> tokens_; - - // The ImageRegistration pointers here are weak. When the object is destroyed, - // it will ask to be removed from this map. - std::map<const void*, ImageRegistration*> images_; - - // The connection to Ash, which may be null in tests. - ash::mojom::ClientImageRegistryPtr registry_; - - DISALLOW_COPY_AND_ASSIGN(BrowserImageRegistrarImpl); -}; - -BrowserImageRegistrarImpl* g_registrar = nullptr; - -BrowserImageRegistrarImpl::BrowserImageRegistrarImpl() { - auto* connection = content::ServiceManagerConnection::GetForProcess(); - if (connection) { - connection->GetConnector()->BindInterface(ash::mojom::kServiceName, - ®istry_); - } -} - -BrowserImageRegistrarImpl::~BrowserImageRegistrarImpl() { - DCHECK(images_.empty()); - DCHECK(tokens_.empty()); -} - -scoped_refptr<ImageRegistration> BrowserImageRegistrarImpl::RegisterImage( - const gfx::ImageSkia& image) { - auto iter = images_.find(image.GetBackingObject()); - if (iter != images_.end()) - return base::WrapRefCounted(iter->second); - - // Keep a local record. - auto token = base::UnguessableToken::Create(); - tokens_[token] = image.GetBackingObject(); - auto refcounted = base::MakeRefCounted<ImageRegistration>(token, image); - images_.insert(std::make_pair(image.GetBackingObject(), refcounted.get())); - - // Register with Ash. - if (registry_) - registry_->RegisterImage(token, image); - - return refcounted; -} - -void BrowserImageRegistrarImpl::ForgetImage( - const base::UnguessableToken& token) { - auto iter = tokens_.find(token); - DCHECK(iter != tokens_.end()); - - images_.erase(iter->second); - tokens_.erase(iter); - - // Un-register with Ash. - if (registry_) - registry_->ForgetImage(token); -} - -// static -void BrowserImageRegistrarImpl::InitOnce() { - static bool registrar_initialized = false; - if (!registrar_initialized) { - DCHECK(!g_registrar); - g_registrar = new BrowserImageRegistrarImpl(); - registrar_initialized = true; - } - - DCHECK(g_registrar); -} - -} // namespace - -// static -void BrowserImageRegistrar::Shutdown() { - delete g_registrar; - g_registrar = nullptr; -} - -ImageRegistration::ImageRegistration(const base::UnguessableToken& token, - const gfx::ImageSkia& image) - : token_(token), image_(image) {} - -ImageRegistration::~ImageRegistration() { - DCHECK(g_registrar); - g_registrar->ForgetImage(token_); -} - -// static -scoped_refptr<ImageRegistration> BrowserImageRegistrar::RegisterImage( - const gfx::ImageSkia& image) { - BrowserImageRegistrarImpl::InitOnce(); - return g_registrar->RegisterImage(image); -} - -// static -std::vector<ImageRegistration*> -BrowserImageRegistrar::GetActiveRegistrationsForTesting() { - BrowserImageRegistrarImpl::InitOnce(); - - DCHECK_EQ(g_registrar->images().size(), g_registrar->tokens().size()); - std::vector<ImageRegistration*> registrations; - for (auto iter : g_registrar->images()) { - registrations.push_back(iter.second); - DCHECK_EQ(1U, g_registrar->tokens().count(registrations.back()->token())); - } - return registrations; -}
diff --git a/chrome/browser/ui/ash/browser_image_registrar.h b/chrome/browser/ui/ash/browser_image_registrar.h deleted file mode 100644 index 16b6455..0000000 --- a/chrome/browser/ui/ash/browser_image_registrar.h +++ /dev/null
@@ -1,60 +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 CHROME_BROWSER_UI_ASH_BROWSER_IMAGE_REGISTRAR_H_ -#define CHROME_BROWSER_UI_ASH_BROWSER_IMAGE_REGISTRAR_H_ - -#include "base/macros.h" - -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/unguessable_token.h" -#include "ui/gfx/image/image_skia.h" - -// This class represents an image that's been registered with Ash. It is ref -// counted so that when multiple callsites want to use the same image, they can -// all hold a reference, and when they all release the reference the -// registration will destruct. -class ImageRegistration : public base::RefCounted<ImageRegistration> { - public: - ImageRegistration(const base::UnguessableToken& token, - const gfx::ImageSkia& image); - - const base::UnguessableToken& token() const { return token_; } - - protected: - friend class base::RefCounted<ImageRegistration>; - - virtual ~ImageRegistration(); - - private: - const base::UnguessableToken token_; - const gfx::ImageSkia image_; - - DISALLOW_COPY_AND_ASSIGN(ImageRegistration); -}; - -// A collection of functions to register and unregister images with Ash. This is -// used in Mash to minimize the duplication of images sent over mojo. -class BrowserImageRegistrar { - public: - // Must be called once when the browser process is exiting. - static void Shutdown(); - - // Gets or creates a registration for the given image. This registers the - // image and token with Ash. The caller should hold onto the returned - // object as long as the image is in use. When all refs to a given - // registration are released, Ash will be informed and the associated token - // will no longer be useful. This function also serves as a way to lazily - // initialize the implementation object. - static scoped_refptr<ImageRegistration> RegisterImage( - const gfx::ImageSkia& image) WARN_UNUSED_RESULT; - - static std::vector<ImageRegistration*> GetActiveRegistrationsForTesting(); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(BrowserImageRegistrar); -}; - -#endif // CHROME_BROWSER_UI_ASH_BROWSER_IMAGE_REGISTRAR_H_
diff --git a/chrome/browser/ui/ash/browser_image_registrar_unittest.cc b/chrome/browser/ui/ash/browser_image_registrar_unittest.cc deleted file mode 100644 index 81c5459..0000000 --- a/chrome/browser/ui/ash/browser_image_registrar_unittest.cc +++ /dev/null
@@ -1,62 +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 "chrome/browser/ui/ash/browser_image_registrar.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/gfx/image/image_unittest_util.h" - -namespace { - -using BrowserImageRegistrarTest = testing::Test; - -TEST_F(BrowserImageRegistrarTest, Basic) { - auto GetRegistrations = - &BrowserImageRegistrar::GetActiveRegistrationsForTesting; - - EXPECT_EQ(0U, GetRegistrations().size()); - - // Register an image. - gfx::ImageSkia image1 = gfx::test::CreateImageSkia(15, 20); - scoped_refptr<ImageRegistration> registration1 = - BrowserImageRegistrar::RegisterImage(image1); - EXPECT_EQ(1U, GetRegistrations().size()); - - // Register a different image. - gfx::ImageSkia image2 = gfx::test::CreateImageSkia(15, 20); - EXPECT_NE(image2.GetBackingObject(), image1.GetBackingObject()); - scoped_refptr<ImageRegistration> registration2 = - BrowserImageRegistrar::RegisterImage(image2); - EXPECT_EQ(2U, GetRegistrations().size()); - EXPECT_NE(registration2->token(), registration1->token()); - - // Register an image that's a copy of one which is already registered. - gfx::ImageSkia image3 = image1; - EXPECT_EQ(image3.GetBackingObject(), image1.GetBackingObject()); - scoped_refptr<ImageRegistration> registration3 = - BrowserImageRegistrar::RegisterImage(image3); - EXPECT_EQ(2U, GetRegistrations().size()); - EXPECT_EQ(registration3->token(), registration1->token()); - - // Get rid of one reference to the twice-registered image and verify the - // registration is still there. - registration1 = nullptr; - EXPECT_EQ(2U, GetRegistrations().size()); - - registration3 = nullptr; - EXPECT_EQ(1U, GetRegistrations().size()); - - // Verify that resetting an image that was registered, thus dropping the - // backing store's refcount, before releasing the registration ref, won't - // cause a crash or any change in the registration list. - image2 = gfx::ImageSkia(); - EXPECT_EQ(1U, GetRegistrations().size()); - - registration2 = nullptr; - EXPECT_EQ(0U, GetRegistrations().size()); - - BrowserImageRegistrar::Shutdown(); -} - -} // namespace
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc index f7774c8f..5adb58f 100644 --- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc +++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -357,7 +357,7 @@ // Crostini Terminals always have their own item. // TODO(rjwright): We shouldn't need to special-case Crostini here. // https://crbug.com/846546 - if (CrostiniAppIdFromAppName(browser->app_name())) + if (crostini::CrostiniAppIdFromAppName(browser->app_name())) return false; // V1 App popup windows may have their own item.
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index e53289312..657bb19 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -250,7 +250,7 @@ app_window_controllers_.push_back(std::move(extension_app_window_controller)); app_window_controllers_.push_back( std::make_unique<ArcAppWindowLauncherController>(this)); - if (IsCrostiniUIAllowedForProfile(profile)) { + if (crostini::IsCrostiniUIAllowedForProfile(profile)) { std::unique_ptr<CrostiniAppWindowShelfController> crostini_controller = std::make_unique<CrostiniAppWindowShelfController>(this); crostini_app_window_shelf_controller_ = crostini_controller.get(); @@ -1143,7 +1143,7 @@ profile_, extension_misc::EXTENSION_ICON_MEDIUM, this); app_icon_loaders_.push_back(std::move(internal_app_icon_loader)); - if (IsCrostiniUIAllowedForProfile(profile_)) { + if (crostini::IsCrostiniUIAllowedForProfile(profile_)) { std::unique_ptr<AppIconLoader> crostini_app_icon_loader = std::make_unique<CrostiniAppIconLoader>( profile_, extension_misc::EXTENSION_ICON_MEDIUM, this); @@ -1177,7 +1177,7 @@ app_updaters_.push_back(std::move(arc_app_updater)); } - if (IsCrostiniUIAllowedForProfile(profile())) { + if (crostini::IsCrostiniUIAllowedForProfile(profile())) { std::unique_ptr<LauncherAppUpdater> crostini_app_updater( new LauncherCrostiniAppUpdater(this, profile())); app_updaters_.push_back(std::move(crostini_app_updater));
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index 648127e9..ae700a0 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -921,7 +921,7 @@ result += "Platform_App"; } else if (app == arc_support_host_->id()) { result += "Play Store"; - } else if (app == kCrostiniTerminalId) { + } else if (app == crostini::kCrostiniTerminalId) { result += "Terminal"; } else { bool arc_app_found = false; @@ -4638,7 +4638,7 @@ TEST_F(ChromeLauncherControllerTest, CrostiniTerminalPinUnpin) { InitLauncherController(); - const std::string app_id = kCrostiniTerminalId; + const std::string app_id = crostini::kCrostiniTerminalId; EXPECT_FALSE(launcher_controller_->IsAppPinned(app_id)); // Load pinned Terminal from prefs without Crostini UI being allowed
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc index 6d8cf08..53005b20 100644 --- a/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc +++ b/chrome/browser/ui/ash/launcher/crostini_app_window_shelf_controller.cc
@@ -165,7 +165,7 @@ Browser* browser = chrome::FindBrowserWithWindow(window); if (browser) { base::Optional<std::string> app_id = - CrostiniAppIdFromAppName(browser->app_name()); + crostini::CrostiniAppIdFromAppName(browser->app_name()); if (!app_id) return; RegisterAppWindow(window, app_id.value());
diff --git a/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc index 2c6dfed7..abf0117 100644 --- a/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc +++ b/chrome/browser/ui/ash/launcher/crostini_shelf_context_menu.cc
@@ -59,7 +59,8 @@ return; if (command_id == ash::MENU_NEW_WINDOW) { - LaunchCrostiniApp(controller()->profile(), item().id.app_id, display_id()); + crostini::LaunchCrostiniApp(controller()->profile(), item().id.app_id, + display_id()); return; } NOTREACHED();
diff --git a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc index 216526b..e728018 100644 --- a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc +++ b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
@@ -174,7 +174,7 @@ crostini::CrostiniRegistryService* registry_service = crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_); if (registry_service && registry_service->IsCrostiniShelfAppId(id)) { - return IsCrostiniUIAllowedForProfile(profile_) && + return crostini::IsCrostiniUIAllowedForProfile(profile_) && registry_service->GetRegistration(id).has_value(); } @@ -218,7 +218,7 @@ // This expects a valid app list id, which is fine as we only get here for // shelf entries associated with an actual app and not arbitrary Crostini // windows. - LaunchCrostiniApp(profile_, app_id, display_id); + crostini::LaunchCrostiniApp(profile_, app_id, display_id); return; }
diff --git a/chrome/browser/ui/cocoa/main_menu_builder.h b/chrome/browser/ui/cocoa/main_menu_builder.h index c399457..03a1837 100644 --- a/chrome/browser/ui/cocoa/main_menu_builder.h +++ b/chrome/browser/ui/cocoa/main_menu_builder.h
@@ -93,6 +93,9 @@ // the one specified here is used instead. MenuItemBuilder& key_equivalent(NSString* key_equivalent, NSEventModifierFlags flags) { + DCHECK((flags & NSEventModifierFlagShift) == 0) + << "The shift modifier flag should be directly applied to the key " + "equivalent."; key_equivalent_ = key_equivalent; key_equivalent_flags_ = flags; return *this;
diff --git a/chrome/browser/ui/cocoa/main_menu_builder.mm b/chrome/browser/ui/cocoa/main_menu_builder.mm index 5e5167e..740e15d4 100644 --- a/chrome/browser/ui/cocoa/main_menu_builder.mm +++ b/chrome/browser/ui/cocoa/main_menu_builder.mm
@@ -141,8 +141,7 @@ Item(IDS_PASTE_MATCH_STYLE_MAC) .action(@selector(pasteAndMatchStyle:)) .is_alternate() - .key_equivalent(@"v", NSEventModifierFlagCommand | - NSEventModifierFlagShift | + .key_equivalent(@"V", NSEventModifierFlagCommand | NSEventModifierFlagOption), Item(IDS_EDIT_DELETE_MAC) .tag(IDC_CONTENT_CONTEXT_DELETE)
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions.h b/chrome/browser/ui/cocoa/nsmenuitem_additions.h index 05129d4..5544e2ab0 100644 --- a/chrome/browser/ui/cocoa/nsmenuitem_additions.h +++ b/chrome/browser/ui/cocoa/nsmenuitem_additions.h
@@ -19,5 +19,6 @@ // Used by tests to set internal state without having to change global input // source. void SetIsInputSourceDvorakQwertyForTesting(bool is_dvorak_qwerty); +void SetIsInputSourceCzechForTesting(bool is_czech); #endif // CHROME_BROWSER_UI_COCOA_NSMENUITEM_ADDITIONS_H_
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm index d2a667b..92bba46 100644 --- a/chrome/browser/ui/cocoa/nsmenuitem_additions.mm +++ b/chrome/browser/ui/cocoa/nsmenuitem_additions.mm
@@ -12,12 +12,17 @@ namespace { bool g_is_input_source_dvorak_qwerty = false; +bool g_is_input_source_czech = false; } // namespace void SetIsInputSourceDvorakQwertyForTesting(bool is_dvorak_qwerty) { g_is_input_source_dvorak_qwerty = is_dvorak_qwerty; } +void SetIsInputSourceCzechForTesting(bool is_czech) { + g_is_input_source_czech = is_czech; +} + @interface KeyboardInputSourceListener : NSObject @end @@ -47,6 +52,9 @@ inputSource.get(), kTISPropertyInputSourceID); g_is_input_source_dvorak_qwerty = [inputSourceID isEqualToString:@"com.apple.keylayout.DVORAK-QWERTYCMD"]; + g_is_input_source_czech = + [inputSourceID rangeOfString:@"com.apple.keylayout.Czech"].location != + NSNotFound; } - (void)inputSourceDidChange:(NSNotification*)notification { @@ -78,6 +86,23 @@ NSUInteger eventModifiers = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask; + // cmd-opt-a gives some weird char as characters and "a" as + // charactersWithoutModifiers with an US layout, but an "a" as characters and + // a weird char as "charactersWithoutModifiers" with a cyrillic layout. Oh, + // Cocoa! Instead of getting the current layout from Text Input Services, + // and then requesting the kTISPropertyUnicodeKeyLayoutData and looking in + // there, let's try a pragmatic hack. + if ([eventString length] == 0 || + ([eventString characterAtIndex:0] > 0x7f && + [[event characters] length] > 0 && + [[event characters] characterAtIndex:0] <= 0x7f)) { + eventString = [event characters]; + + // Process the shift if necessary. + if (eventModifiers & NSShiftKeyMask) + eventString = [eventString uppercaseString]; + } + if ([eventString length] == 0 || [[self keyEquivalent] length] == 0) return NO; @@ -110,22 +135,6 @@ eventModifiers |= NSFunctionKeyMask; } - // cmd-opt-a gives some weird char as characters and "a" as - // charactersWithoutModifiers with an US layout, but an "a" as characters and - // a weird char as "charactersWithoutModifiers" with a cyrillic layout. Oh, - // Cocoa! Instead of getting the current layout from Text Input Services, - // and then requesting the kTISPropertyUnicodeKeyLayoutData and looking in - // there, let's try a pragmatic hack. - if ([eventString characterAtIndex:0] > 0x7f && - [[event characters] length] > 0 && - [[event characters] characterAtIndex:0] <= 0x7f) { - eventString = [event characters]; - - // Process the shift if necessary. - if (eventModifiers & NSShiftKeyMask) - eventString = [eventString uppercaseString]; - } - // We intentionally leak this object. static __attribute__((unused)) KeyboardInputSourceListener* listener = [[KeyboardInputSourceListener alloc] init]; @@ -165,6 +174,17 @@ NSAlternateKeyMask | NSShiftKeyMask; + // On Czech keyboards, we want to interpret cmd + '+' as cmd + '1'. + // htts://crbug.com/889424. We don't need special handling for other numeric + // keys because they produce non-ASCII characters, and we already have logic + // that ignores non-ASCII characters in favor of modified characters. + if (g_is_input_source_czech) { + if (eventModifiers == NSCommandKeyMask && + [eventString isEqualToString:@"+"]) { + eventString = @"1"; + } + } + return [eventString isEqualToString:[self keyEquivalent]] && eventModifiers == [self keyEquivalentModifierMask]; }
diff --git a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm index 5bd4a29..2060d3d 100644 --- a/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm +++ b/chrome/browser/ui/cocoa/nsmenuitem_additions_unittest.mm
@@ -331,6 +331,26 @@ /*compareCocoa=*/false); ExpectKeyFiresItem(key, MenuItem(@"T", NSCommandKeyMask), /*compareCocoa=*/false); + + // On Czech layout, cmd + '+' should instead trigger cmd + '1'. + key = KeyEvent(0x100108, @"1", @"+", 18); + ExpectKeyDoesntFireItem(key, MenuItem(@"1", NSCommandKeyMask), + /*compareCocoa=*/false); + SetIsInputSourceCzechForTesting(true); + ExpectKeyFiresItem(key, MenuItem(@"1", NSCommandKeyMask), + /*compareCocoa=*/false); + SetIsInputSourceCzechForTesting(false); + ExpectKeyDoesntFireItem(key, MenuItem(@"1", NSCommandKeyMask), + /*compareCocoa=*/false); + + // On Vietnamese layout, cmd + '' [vkeycode = 18] should instead trigger cmd + + // '1'. Ditto for other number keys. + key = KeyEvent(0x100108, @"1", @"", 18); + ExpectKeyFiresItem(key, MenuItem(@"1", NSCommandKeyMask), + /*compareCocoa=*/false); + key = KeyEvent(0x100108, @"4", @"", 21); + ExpectKeyFiresItem(key, MenuItem(@"4", NSCommandKeyMask), + /*compareCocoa=*/false); } NSString* keyCodeToCharacter(NSUInteger keyCode,
diff --git a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc index 370b911..7f5854b0 100644 --- a/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc +++ b/chrome/browser/ui/views/crostini/crostini_browser_test_util.cc
@@ -74,7 +74,7 @@ void CrostiniDialogBrowserTest::SetUp() { InitCrosTermina(); - SetCrostiniUIAllowedForTesting(true); + crostini::SetCrostiniUIAllowedForTesting(true); DialogBrowserTest::SetUp(); }
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc index 137f2f9..b3354d2 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -81,14 +81,16 @@ } // namespace -void ShowCrostiniInstallerView(Profile* profile, CrostiniUISurface ui_surface) { +void crostini::ShowCrostiniInstallerView( + Profile* profile, + crostini::CrostiniUISurface ui_surface) { base::UmaHistogramEnumeration(kCrostiniSetupSourceHistogram, ui_surface, - CrostiniUISurface::kCount); + crostini::CrostiniUISurface::kCount); return CrostiniInstallerView::Show(profile); } void CrostiniInstallerView::Show(Profile* profile) { - DCHECK(IsCrostiniUIAllowedForProfile(profile)); + DCHECK(crostini::IsCrostiniUIAllowedForProfile(profile)); if (!g_crostini_installer_view) { g_crostini_installer_view = new CrostiniInstallerView(profile); views::DialogDelegate::CreateDialogWidget(g_crostini_installer_view, @@ -162,7 +164,8 @@ // Kick off the Crostini Restart sequence. We will be added as an observer. restart_id_ = crostini::CrostiniManager::GetForProfile(profile_)->RestartCrostini( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::kCrostiniDefaultVmName, + crostini::kCrostiniDefaultContainerName, base::BindOnce(&CrostiniInstallerView::MountContainerFinished, weak_ptr_factory_.GetWeakPtr()), this); @@ -448,9 +451,9 @@ crostini::CrostiniManager* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_); - crostini_manager->LaunchContainerTerminal(kCrostiniDefaultVmName, - kCrostiniDefaultContainerName, - std::vector<std::string>()); + crostini_manager->LaunchContainerTerminal( + crostini::kCrostiniDefaultVmName, crostini::kCrostiniDefaultContainerName, + std::vector<std::string>()); StepProgress(); RecordSetupResultHistogram(SetupResult::kSuccess);
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc index 433398ec3..10ba78ba 100644 --- a/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc +++ b/chrome/browser/ui/views/crostini/crostini_installer_view_browsertest.cc
@@ -91,7 +91,7 @@ // CrostiniDialogBrowserTest: void ShowUi(const std::string& name) override { ShowCrostiniInstallerView(browser()->profile(), - CrostiniUISurface::kSettings); + crostini::CrostiniUISurface::kSettings); } void SetUpOnMainThread() override {
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc index 031dc5c8..34e4de2a 100644 --- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
@@ -31,15 +31,16 @@ } // namespace -void ShowCrostiniUninstallerView(Profile* profile, - CrostiniUISurface ui_surface) { +void crostini::ShowCrostiniUninstallerView( + Profile* profile, + crostini::CrostiniUISurface ui_surface) { base::UmaHistogramEnumeration(kCrostiniUninstallSourceHistogram, ui_surface, - CrostiniUISurface::kCount); + crostini::CrostiniUISurface::kCount); return CrostiniUninstallerView::Show(profile); } void CrostiniUninstallerView::Show(Profile* profile) { - DCHECK(IsCrostiniUIAllowedForProfile(profile)); + DCHECK(crostini::IsCrostiniUIAllowedForProfile(profile)); if (!g_crostini_uninstaller_view) { g_crostini_uninstaller_view = new CrostiniUninstallerView(profile); views::DialogDelegate::CreateDialogWidget(g_crostini_uninstaller_view, @@ -85,7 +86,7 @@ // Kick off the Crostini Remove sequence. crostini::CrostiniManager::GetForProfile(profile_)->RemoveCrostini( - kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + crostini::kCrostiniDefaultVmName, crostini::kCrostiniDefaultContainerName, base::BindOnce(&CrostiniUninstallerView::UninstallCrostiniFinished, weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc index ab6a48e..4b8f1d9b 100644 --- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc +++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view_browsertest.cc
@@ -60,7 +60,7 @@ // DialogBrowserTest: void ShowUi(const std::string& name) override { ShowCrostiniUninstallerView(browser()->profile(), - CrostiniUISurface::kSettings); + crostini::CrostiniUISurface::kSettings); } CrostiniUninstallerView* ActiveView() {
diff --git a/chrome/browser/ui/views/crostini/crostini_upgrade_view.cc b/chrome/browser/ui/views/crostini/crostini_upgrade_view.cc index d5f7641..c1cec7a2 100644 --- a/chrome/browser/ui/views/crostini/crostini_upgrade_view.cc +++ b/chrome/browser/ui/views/crostini/crostini_upgrade_view.cc
@@ -29,14 +29,15 @@ } // namespace -void ShowCrostiniUpgradeView(Profile* profile, CrostiniUISurface ui_surface) { +void crostini::ShowCrostiniUpgradeView(Profile* profile, + crostini::CrostiniUISurface ui_surface) { base::UmaHistogramEnumeration(kCrostiniUpgradeSourceHistogram, ui_surface, - CrostiniUISurface::kCount); + crostini::CrostiniUISurface::kCount); return CrostiniUpgradeView::Show(profile); } void CrostiniUpgradeView::Show(Profile* profile) { - DCHECK(IsCrostiniUIAllowedForProfile(profile)); + DCHECK(crostini::IsCrostiniUIAllowedForProfile(profile)); if (!g_crostini_upgrade_view) { g_crostini_upgrade_view = new CrostiniUpgradeView; CreateDialogWidget(g_crostini_upgrade_view, nullptr, nullptr);
diff --git a/chrome/browser/ui/views/crostini/crostini_upgrade_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_upgrade_view_browsertest.cc index a46eec5..4b3e41a 100644 --- a/chrome/browser/ui/views/crostini/crostini_upgrade_view_browsertest.cc +++ b/chrome/browser/ui/views/crostini/crostini_upgrade_view_browsertest.cc
@@ -26,7 +26,8 @@ // DialogBrowserTest: void ShowUi(const std::string& name) override { - ShowCrostiniUpgradeView(browser()->profile(), CrostiniUISurface::kAppList); + ShowCrostiniUpgradeView(browser()->profile(), + crostini::CrostiniUISurface::kAppList); } CrostiniUpgradeView* ActiveView() { @@ -86,7 +87,9 @@ histogram_tester.ExpectUniqueSample( "Crostini.UpgradeSource", - static_cast<base::HistogramBase::Sample>(CrostiniUISurface::kAppList), 1); + static_cast<base::HistogramBase::Sample>( + crostini::CrostiniUISurface::kAppList), + 1); } IN_PROC_BROWSER_TEST_F(CrostiniUpgradeViewBrowserTest, @@ -98,7 +101,8 @@ ExpectNoView(); UnregisterTermina(); - LaunchCrostiniApp(browser()->profile(), kCrostiniTerminalId, 0); + crostini::LaunchCrostiniApp(browser()->profile(), + crostini::kCrostiniTerminalId, 0); ExpectNoView(); } @@ -112,7 +116,8 @@ ExpectNoView(); UnregisterTermina(); - LaunchCrostiniApp(browser()->profile(), kCrostiniTerminalId, 0); + crostini::LaunchCrostiniApp(browser()->profile(), + crostini::kCrostiniTerminalId, 0); ExpectView(); ActiveView()->GetDialogClientView()->AcceptWindow(); @@ -122,5 +127,7 @@ histogram_tester.ExpectUniqueSample( "Crostini.UpgradeSource", - static_cast<base::HistogramBase::Sample>(CrostiniUISurface::kAppList), 1); + static_cast<base::HistogramBase::Sample>( + crostini::CrostiniUISurface::kAppList), + 1); }
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc index 1adb1ff..c3f7c23 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -168,20 +168,12 @@ const ExtensionInstallPrompt::DoneCallback& done_callback, std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - bool use_tab_modal_dialog = prompt->ShouldUseTabModalDialog(); ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView( show_params->profile(), show_params->GetParentWebContents(), done_callback, std::move(prompt)); - if (use_tab_modal_dialog) { - content::WebContents* parent_web_contents = - show_params->GetParentWebContents(); - if (parent_web_contents) - constrained_window::ShowWebModalDialogViews(dialog, parent_web_contents); - } else { - constrained_window::CreateBrowserModalDialogViews( - dialog, show_params->GetParentWindow()) - ->Show(); - } + constrained_window::CreateBrowserModalDialogViews( + dialog, show_params->GetParentWindow()) + ->Show(); } // A custom scrollable view implementation for the dialog. @@ -437,8 +429,7 @@ } ui::ModalType ExtensionInstallDialogView::GetModalType() const { - return prompt_->ShouldUseTabModalDialog() ? ui::MODAL_TYPE_CHILD - : ui::MODAL_TYPE_WINDOW; + return ui::MODAL_TYPE_WINDOW; } void ExtensionInstallDialogView::LinkClicked(views::Link* source,
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc index af5870f..ca6fd7b8 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc
@@ -294,7 +294,7 @@ private: ExtensionInstallPrompt::PromptType type_ = - ExtensionInstallPrompt::INLINE_INSTALL_PROMPT; + ExtensionInstallPrompt::INSTALL_PROMPT; bool from_webstore_ = false; PermissionMessages permissions_; std::vector<base::FilePath> retained_files_; @@ -330,12 +330,14 @@ IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewInteractiveBrowserTest, InvokeUi_FromWebstore) { + set_type(ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT); set_from_webstore(); ShowAndVerifyUi(); } IN_PROC_BROWSER_TEST_F(ExtensionInstallDialogViewInteractiveBrowserTest, InvokeUi_FromWebstoreWithPermission) { + set_type(ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT); set_from_webstore(); AddPermission("Example permission"); ShowAndVerifyUi(); @@ -421,7 +423,7 @@ "Testing with %d ratings, %f average rating. Expected text: '%s'.", num_ratings, average_rating, expected_text.c_str())); std::unique_ptr<ExtensionInstallPrompt::Prompt> prompt = - CreatePrompt(ExtensionInstallPrompt::INLINE_INSTALL_PROMPT); + CreatePrompt(ExtensionInstallPrompt::REPAIR_PROMPT); prompt->SetWebstoreData("1,234", true, average_rating, num_ratings); ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView(
diff --git a/chrome/browser/ui/views/frame/browser_frame_mash.cc b/chrome/browser/ui/views/frame/browser_frame_mash.cc index c0059ae..15f9be8 100644 --- a/chrome/browser/ui/views/frame/browser_frame_mash.cc +++ b/chrome/browser/ui/views/frame/browser_frame_mash.cc
@@ -58,9 +58,9 @@ params.delegate = browser_view_; std::map<std::string, std::vector<uint8_t>> properties = views::MusClient::ConfigurePropertiesFromParams(params); - // Indicates mash shouldn't handle immersive, rather we will. - properties[ws::mojom::WindowManager::kDisableImmersive_InitProperty] = - mojo::ConvertTo<std::vector<uint8_t>>(true); + + // The client will draw the frame. + params.remove_standard_frame = true; // ChromeLauncherController manages the browser shortcut shelf item; set the // window's shelf item type property to be ignored by ash::ShelfWindowWatcher.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc index 4360480..9b397fc 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -186,17 +186,6 @@ static_cast<int>(browser->is_app() ? ash::AppType::CHROME_APP : ash::AppType::BROWSER)); - // In tablet mode, to prevent accidental taps of the window controls, and to - // give more horizontal space for tabs and the new tab button especially in - // splitscreen view, we hide the window controls. We only do this when the - // Home Launcher feature is enabled, since it gives the user the ability to - // minimize all windows when pressing the Launcher button on the shelf. - window->SetProperty( - ash::kHideCaptionButtonsInTabletModeKey, - (browser->is_app() || !app_list_features::IsHomeLauncherEnabled()) - ? false - : true); - window_observer_.Add(GetFrameWindow()); // To preserve privacy, tag incognito windows so that they won't be included @@ -452,8 +441,6 @@ void BrowserNonClientFrameViewAsh::ChildPreferredSizeChanged( views::View* child) { - // This is required even when Ash provides the header (as in Mash) for the - // |hosted_app_button_container_|. if (browser_view()->initialized()) { InvalidateLayout(); frame()->GetRootView()->Layout(); @@ -744,9 +731,15 @@ // BrowserNonClientFrameViewAsh, private: bool BrowserNonClientFrameViewAsh::ShouldShowCaptionButtons() const { - if (frame()->GetNativeWindow()->GetProperty( - ash::kHideCaptionButtonsInTabletModeKey) && - TabletModeClient::Get() && + // In tablet mode, to prevent accidental taps of the window controls, and to + // give more horizontal space for tabs and the new tab button especially in + // splitscreen view, we hide the window controls. We only do this when the + // Home Launcher feature is enabled, since it gives the user the ability to + // minimize all windows when pressing the Launcher button on the shelf. + const bool hide_caption_buttons_in_tablet_mode = + !browser_view()->browser()->is_app() && + app_list_features::IsHomeLauncherEnabled(); + if (hide_caption_buttons_in_tablet_mode && TabletModeClient::Get() && TabletModeClient::Get()->tablet_mode_enabled()) { return false; } @@ -823,11 +816,8 @@ base::Optional<SkColor> theme_color = browser->hosted_app_controller()->GetThemeColor(); if (theme_color) { - frame()->GetNativeWindow()->SetProperty( - ash::kFrameIsThemedByHostedAppKey, - !!browser->hosted_app_controller()->GetThemeColor()); + header->set_button_color_mode(ash::FrameCaptionButton::ColorMode::kThemed); header->SetFrameColors(*theme_color, *theme_color); - active_color = ash::FrameCaptionButton::GetButtonColor( ash::FrameCaptionButton::ColorMode::kThemed, *theme_color); } @@ -852,7 +842,9 @@ IsForExperimentalHostedAppBrowser(browser_view()->browser())) { active_color = browser_view()->browser()->hosted_app_controller()->GetThemeColor(); - window->SetProperty(ash::kFrameIsThemedByHostedAppKey, !!active_color); + frame_header_->set_button_color_mode( + active_color ? ash::FrameCaptionButton::ColorMode::kThemed + : ash::FrameCaptionButton::ColorMode::kDefault); } else if (!browser_view()->browser()->is_app()) { active_color = kMdWebUiFrameColor; }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h index d816efb9..eee2cbb 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -247,11 +247,6 @@ // Owned by views hierarchy. HostedAppButtonContainer* hosted_app_button_container_ = nullptr; - // A view that contains the extra views used for hosted apps - // (|hosted_app_button_container_| and |hosted_app_origin_text_|). - // Only used in Mash. - views::View* hosted_app_extras_container_ = nullptr; - // Ash's mojom::SplitViewController. ash::mojom::SplitViewControllerPtr split_view_controller_;
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc index 3ebc0bd..733c986e 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -144,22 +144,17 @@ GetOmniboxStateAlpha(OmniboxPartState::SELECTED)); } - // Bubbles are given the full internal height of the location bar so that all - // child views in the location bar have the same height. The visible height of - // the bubble should be smaller, so use an empty border to shrink down the - // content bounds so the background gets painted correctly. - SetBorder(views::CreateEmptyBorder( - gfx::Insets(GetLayoutConstant(LOCATION_BAR_BUBBLE_VERTICAL_PADDING), - GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING).left()))); + UpdateBorder(); set_notify_enter_exit_on_child(true); // Flip the canvas in RTL so the separator is drawn on the correct side. separator_view_->EnableCanvasFlippingForRTLUI(true); + + md_observer_.Add(ui::MaterialDesignController::GetInstance()); } -IconLabelBubbleView::~IconLabelBubbleView() { -} +IconLabelBubbleView::~IconLabelBubbleView() {} void IconLabelBubbleView::InkDropAnimationStarted() { separator_view_->UpdateOpacity(); @@ -218,6 +213,16 @@ return false; } +void IconLabelBubbleView::UpdateBorder() { + // Bubbles are given the full internal height of the location bar so that all + // child views in the location bar have the same height. The visible height of + // the bubble should be smaller, so use an empty border to shrink down the + // content bounds so the background gets painted correctly. + SetBorder(views::CreateEmptyBorder( + gfx::Insets(GetLayoutConstant(LOCATION_BAR_BUBBLE_VERTICAL_PADDING), + GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING).left()))); +} + gfx::Size IconLabelBubbleView::CalculatePreferredSize() const { // Height will be ignored by the LocationBarView. return GetSizeForLabelWidth(label_->GetPreferredSize().width()); @@ -419,6 +424,15 @@ AnimationEnded(animation); } +void IconLabelBubbleView::OnMdModeChanged() { + UpdateBorder(); + + // PreferredSizeChanged() incurs an expensive layout of the location bar, so + // only call it when this view is showing. + if (visible()) + PreferredSizeChanged(); +} + SkColor IconLabelBubbleView::GetParentBackgroundColor() const { return GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_TextfieldDefaultBackground);
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h index dfe8240..2b8600f 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -10,7 +10,9 @@ #include "base/macros.h" #include "base/optional.h" +#include "base/scoped_observer.h" #include "base/strings/string16.h" +#include "ui/base/material_design/material_design_controller_observer.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop_host_view.h" @@ -33,7 +35,8 @@ // base for the classes that handle the location icon (including the EV bubble), // tab-to-search UI, and content settings. class IconLabelBubbleView : public views::InkDropObserver, - public views::Button { + public views::Button, + public ui::MaterialDesignControllerObserver { public: static constexpr int kTrailingPaddingPreMd = 2; @@ -122,6 +125,9 @@ // prevent the bubble from reshowing on a mouse release. virtual bool IsBubbleShowing() const; + // Sets the border padding around this view. + virtual void UpdateBorder(); + // views::InkDropHostView: gfx::Size CalculatePreferredSize() const override; void Layout() override; @@ -147,6 +153,9 @@ void AnimationProgressed(const gfx::Animation* animation) override; void AnimationCanceled(const gfx::Animation* animation) override; + // ui::MaterialDesignControllerObserver: + void OnMdModeChanged() override; + const gfx::FontList& font_list() const { return label_->font_list(); } SkColor GetParentBackgroundColor() const; @@ -246,6 +255,10 @@ double pause_animation_state_ = 0.0; double open_state_fraction_ = 0.0; + ScopedObserver<ui::MaterialDesignController, + ui::MaterialDesignControllerObserver> + md_observer_{this}; + DISALLOW_COPY_AND_ASSIGN(IconLabelBubbleView); };
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index 2b2a3ba8..2d65470e 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -156,6 +156,7 @@ bookmarks::prefs::kEditBookmarksEnabled, profile->GetPrefs(), base::Bind(&LocationBarView::UpdateWithoutTabRestore, base::Unretained(this))); + md_observer_.Add(ui::MaterialDesignController::GetInstance()); } LocationBarView::~LocationBarView() {} @@ -659,10 +660,8 @@ } void LocationBarView::ChildPreferredSizeChanged(views::View* child) { - if (child != page_action_icon_container_view_) - return; - Layout(); + SchedulePaint(); } void LocationBarView::Update(const WebContents* contents) { @@ -1330,6 +1329,15 @@ } //////////////////////////////////////////////////////////////////////////////// +// LocationBarView, private ui::MaterialDesignControllerObserver implementation: + +void LocationBarView::OnMdModeChanged() { + RefreshLocationIcon(); + Layout(); + SchedulePaint(); +} + +//////////////////////////////////////////////////////////////////////////////// // LocationBarView, private static methods: // static
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h index 64d636a..e441ae82 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.h +++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -26,6 +26,7 @@ #include "chrome/browser/ui/views/page_action/page_action_icon_view.h" #include "components/prefs/pref_member.h" #include "components/security_state/core/security_state.h" +#include "ui/base/material_design/material_design_controller_observer.h" #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/font.h" @@ -78,7 +79,8 @@ public DropdownBarHostDelegate, public views::ButtonListener, public ContentSettingImageView::Delegate, - public PageActionIconView::Delegate { + public PageActionIconView::Delegate, + public ui::MaterialDesignControllerObserver { public: class Delegate { public: @@ -386,6 +388,9 @@ // DropdownBarHostDelegate: void SetFocusAndSelection(bool select_all) override; + // ui::MaterialDesignControllerObserver: + void OnMdModeChanged() override; + // Returns the total amount of space reserved above or below the content, // which is the vertical edge thickness plus the padding next to it. static int GetTotalVerticalPadding(); @@ -488,6 +493,10 @@ // The focus ring, if one is in use. std::unique_ptr<views::FocusRing> focus_ring_; + ScopedObserver<ui::MaterialDesignController, + ui::MaterialDesignControllerObserver> + md_observer_{this}; + // Used to scope the lifetime of asynchronous icon fetch callbacks to the // lifetime of the object. Weak pointers issued by this factory are // invalidated whenever we start a new icon fetch, so don't use this weak
diff --git a/chrome/browser/ui/views/media_router/media_router_views_ui.cc b/chrome/browser/ui/views/media_router/media_router_views_ui.cc index 34382a41..ce0b6f0 100644 --- a/chrome/browser/ui/views/media_router/media_router_views_ui.cc +++ b/chrome/browser/ui/views/media_router/media_router_views_ui.cc
@@ -147,6 +147,10 @@ : UIMediaSinkState::AVAILABLE; ui_sink.cast_modes = sink.cast_modes; } + if (ui_sink.icon_type == SinkIconType::HANGOUT && + ui_sink.state == UIMediaSinkState::AVAILABLE && sink.sink.domain()) { + ui_sink.status_text = base::UTF8ToUTF16(*sink.sink.domain()); + } if (issue && IssueMatches(*issue, ui_sink)) ui_sink.issue = issue; return ui_sink;
diff --git a/chrome/browser/ui/views/media_router/media_router_views_ui.h b/chrome/browser/ui/views/media_router/media_router_views_ui.h index a9a8286..2843a42c1 100644 --- a/chrome/browser/ui/views/media_router/media_router_views_ui.h +++ b/chrome/browser/ui/views/media_router/media_router_views_ui.h
@@ -37,6 +37,7 @@ FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, ConnectingState); FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, DisconnectingState); FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, AddAndRemoveIssue); + FRIEND_TEST_ALL_PREFIXES(MediaRouterViewsUITest, ShowDomainForHangouts); // MediaRouterUIBase: void InitCommon(content::WebContents* initiator) override;
diff --git a/chrome/browser/ui/views/media_router/media_router_views_ui_unittest.cc b/chrome/browser/ui/views/media_router/media_router_views_ui_unittest.cc index fe5fdecb..56c929ab 100644 --- a/chrome/browser/ui/views/media_router/media_router_views_ui_unittest.cc +++ b/chrome/browser/ui/views/media_router/media_router_views_ui_unittest.cc
@@ -279,4 +279,39 @@ ui_->RemoveObserver(&observer); } +TEST_F(MediaRouterViewsUITest, ShowDomainForHangouts) { + const std::string domain1 = "domain1.com"; + const std::string domain2 = "domain2.com"; + MediaSinkWithCastModes available_hangout( + MediaSink("sink1", "Hangout 1", SinkIconType::HANGOUT)); + MediaSinkWithCastModes connected_hangout( + MediaSink("sink2", "Hangout 2", SinkIconType::HANGOUT)); + available_hangout.sink.set_domain(domain1); + connected_hangout.sink.set_domain(domain2); + available_hangout.cast_modes = {MediaCastMode::TAB_MIRROR}; + connected_hangout.cast_modes = {MediaCastMode::TAB_MIRROR}; + + MockControllerObserver observer; + ui_->AddObserver(&observer); + const std::string route_description = "route 1"; + MediaRoute route(kRouteId, MediaSource(kSourceId), "sink2", route_description, + true, true); + ui_->OnRoutesUpdated({route}, {}); + + // The domain should be used as the status text only if the sink is available. + // If the sink has a route, the route description is used. + EXPECT_CALL(observer, OnModelUpdated(_)) + .WillOnce(WithArg<0>([&](const CastDialogModel& model) { + EXPECT_EQ(2u, model.media_sinks().size()); + EXPECT_EQ(model.media_sinks()[0].id, available_hangout.sink.id()); + EXPECT_EQ(base::UTF8ToUTF16(domain1), + model.media_sinks()[0].status_text); + EXPECT_EQ(model.media_sinks()[1].id, connected_hangout.sink.id()); + EXPECT_EQ(base::UTF8ToUTF16(route_description), + model.media_sinks()[1].status_text); + })); + ui_->OnResultsUpdated({available_hangout, connected_hangout}); + ui_->RemoveObserver(&observer); +} + } // namespace media_router
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc index 838e816..a7d3b64 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc +++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -53,8 +53,6 @@ command_id_(command_id), active_(false), suppress_mouse_released_action_(false) { - SetBorder(views::CreateEmptyBorder( - GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING))); if (ui::MaterialDesignController::IsNewerMaterialUi()) { // Ink drop ripple opacity. set_ink_drop_visible_opacity( @@ -246,6 +244,17 @@ IconLabelBubbleView::OnBoundsChanged(previous_bounds); } +void PageActionIconView::OnMdModeChanged() { + icon_size_ = GetLayoutConstant(LOCATION_BAR_ICON_SIZE); + UpdateIconImage(); + IconLabelBubbleView::OnMdModeChanged(); +} + +void PageActionIconView::UpdateBorder() { + SetBorder(views::CreateEmptyBorder( + GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING))); +} + void PageActionIconView::SetIconColor(SkColor icon_color) { icon_color_ = icon_color; UpdateIconImage();
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.h b/chrome/browser/ui/views/page_action/page_action_icon_view.h index 0f6d3411..c868b517 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_view.h +++ b/chrome/browser/ui/views/page_action/page_action_icon_view.h
@@ -120,8 +120,10 @@ // Gets the given vector icon in the correct color and size based on |active|. virtual const gfx::VectorIcon& GetVectorIcon() const = 0; - // views::View: + // IconLabelBubbleView: void OnBoundsChanged(const gfx::Rect& previous_bounds) override; + void OnMdModeChanged() override; + void UpdateBorder() override; // Updates the icon image after some state has changed. void UpdateIconImage();
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index 99b01963..9f2ef77 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -759,7 +759,7 @@ GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo( GetAttachedBrowserWidget()->GetNativeView(), target_tabstrip->GetWidget()->GetNativeView(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + ui::TransferTouchesBehavior::kDontCancel); #endif if (is_dragging_window_) { @@ -1236,7 +1236,7 @@ gfx::NativeView attached_native_view = attached_widget->GetNativeView(); attached_widget->GetGestureRecognizer()->TransferEventsTo( attached_native_view, dragged_widget->GetNativeView(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + ui::TransferTouchesBehavior::kDontCancel); #endif Detach(can_release_capture_ ? RELEASE_CAPTURE : DONT_RELEASE_CAPTURE);
diff --git a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc index 669c255..c11ff8f 100644 --- a/chrome/browser/ui/webui/extensions/extensions_internals_source.cc +++ b/chrome/browser/ui/webui/extensions/extensions_internals_source.cc
@@ -5,17 +5,22 @@ #include "chrome/browser/ui/webui/extensions/extensions_internals_source.h" #include <string> +#include <unordered_map> #include <utility> #include "base/json/json_writer.h" #include "base/logging.h" #include "base/memory/ref_counted_memory.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_piece.h" #include "base/values.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/webui_url_constants.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/activity.h" +#include "extensions/browser/event_listener_map.h" +#include "extensions/browser/event_router.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/process_manager.h" @@ -77,11 +82,78 @@ return ""; } +// The JSON we generate looks like this: +// +// [ { +// "event_listeners": { +// "count": 2, +// "events": [ { +// "name": "runtime.onInstalled" +// }, { +// "name": "runtime.onSuspend" +// } ] +// }, +// "id": "bhloflhklmhfpedakmangadcdofhnnoh", +// "keepalive": { +// "activities": [ { +// "extra_data": "render-frame", +// "type": "PROCESS_MANAGER" +// } ], +// "count": 1 +// }, +// "location": "INTERNAL", +// "manifest_version": 2, +// "name": "Earth View from Google Earth", +// "path": "/user/Extensions/bhloflhklmhfpedakmangadcdofhnnoh/2.18.5_0", +// "type": "TYPE_EXTENSION", +// "version": "2.18.5" +// } ] +// +// Which is: +// +// LIST +// DICT +// "event_listeners": DICT +// "count": INT +// "events": LIST +// DICT +// "name": STRING +// "filter": DICT +// "id": STRING +// "keepalive": DICT +// "activities": LIST +// DICT +// "extra_data": STRING +// "type": STRING +// "count": INT +// "location": STRING +// "manifest_version": INT +// "name": STRING +// "path": STRING +// "type": STRING +// "version": STRING + +constexpr base::StringPiece kActivitesKey = "activites"; +constexpr base::StringPiece kCountKey = "count"; +constexpr base::StringPiece kEventsKey = "events"; +constexpr base::StringPiece kEventsListenersKey = "event_listeners"; +constexpr base::StringPiece kExtraDataKey = "extra_data"; +constexpr base::StringPiece kFilterKey = "filter"; +constexpr base::StringPiece kKeepaliveKey = "keepalive"; +constexpr base::StringPiece kLocationKey = "location"; +constexpr base::StringPiece kIdKey = "id"; +constexpr base::StringPiece kManifestVersionKey = "manifest_version"; +constexpr base::StringPiece kNameKey = "name"; +constexpr base::StringPiece kPathKey = "path"; +constexpr base::StringPiece kTypeKey = "type"; +constexpr base::StringPiece kVersionKey = "version"; + base::Value FormatKeepaliveData(extensions::ProcessManager* process_manager, const extensions::Extension* extension) { base::Value keepalive_data(base::Value::Type::DICTIONARY); keepalive_data.SetKey( - "count", base::Value(process_manager->GetLazyKeepaliveCount(extension))); + kCountKey, + base::Value(process_manager->GetLazyKeepaliveCount(extension))); const extensions::ProcessManager::ActivitiesMultiset activities = process_manager->GetLazyKeepaliveActivities(extension); base::Value activities_data(base::Value::Type::LIST); @@ -89,14 +161,64 @@ for (const auto& activity : activities) { base::Value activities_entry(base::Value::Type::DICTIONARY); activities_entry.SetKey( - "type", base::Value(extensions::Activity::ToString(activity.first))); - activities_entry.SetKey("extra_data", base::Value(activity.second)); + kTypeKey, base::Value(extensions::Activity::ToString(activity.first))); + activities_entry.SetKey(kExtraDataKey, base::Value(activity.second)); activities_data.GetList().push_back(std::move(activities_entry)); } - keepalive_data.SetKey("activites", std::move(activities_data)); + keepalive_data.SetKey(kActivitesKey, std::move(activities_data)); return keepalive_data; } +void AddEventListenerData(extensions::EventRouter* event_router, + base::Value* data) { + CHECK(data->is_list()); + // A map of extension ID to the event data for that extension, + // which is of type LIST of DICTIONARY. + std::unordered_map<base::StringPiece, base::Value, base::StringPieceHash> + events_map; + + // Build the map of extension IDs to the list of events. + for (const auto& entry : event_router->listeners().listeners()) { + for (const auto& listener_entry : entry.second) { + auto& events_list = events_map[listener_entry->extension_id()]; + if (events_list.is_none()) { + // Not there, so make it a LIST. + events_list = base::Value(base::Value::Type::LIST); + } + // The data for each event is a dictionary, with a name and a + // filter. + base::Value event_data(base::Value::Type::DICTIONARY); + event_data.SetKey(kNameKey, base::Value(listener_entry->event_name())); + // Add the filter if one exists. + base::Value* const filter = listener_entry->filter(); + if (filter != nullptr) { + event_data.SetKey(kFilterKey, filter->Clone()); + } + events_list.GetList().push_back(std::move(event_data)); + } + } + + // Move all of the entries from the map into the output data. + for (auto& output_entry : data->GetList()) { + const base::Value* const value = output_entry.FindKey(kIdKey); + CHECK(value && value->is_string()); + const auto it = events_map.find(value->GetString()); + base::Value listeners(base::Value::Type::DICTIONARY); + if (it == events_map.end()) { + // We didn't find any events, so initialize an empty dictionary. + listeners.SetKey(kCountKey, base::Value(0)); + listeners.SetKey(kEventsKey, base::Value(base::Value::Type::LIST)); + } else { + // Set the count and the events values. + listeners.SetKey( + kCountKey, + base::Value(base::checked_cast<int>(it->second.GetList().size()))); + listeners.SetKey(kEventsKey, std::move(it->second)); + } + output_entry.SetKey(kEventsListenersKey, std::move(listeners)); + } +} + } // namespace ExtensionsInternalsSource::ExtensionsInternalsSource(Profile* profile) @@ -131,23 +253,26 @@ base::Value data(base::Value::Type::LIST); for (const auto& extension : *extensions) { base::Value extension_data(base::Value::Type::DICTIONARY); - extension_data.SetKey("id", base::Value(extension->id())); + extension_data.SetKey(kIdKey, base::Value(extension->id())); extension_data.SetKey( - "keepalive", FormatKeepaliveData(process_manager, extension.get())); - extension_data.SetKey("location", + kKeepaliveKey, FormatKeepaliveData(process_manager, extension.get())); + extension_data.SetKey(kLocationKey, base::Value(LocationToString(extension->location()))); - extension_data.SetKey("manifest_version", + extension_data.SetKey(kManifestVersionKey, base::Value(extension->manifest_version())); - extension_data.SetKey("name", base::Value(extension->name())); - extension_data.SetKey("path", + extension_data.SetKey(kNameKey, base::Value(extension->name())); + extension_data.SetKey(kPathKey, base::Value(extension->path().LossyDisplayName())); - extension_data.SetKey("type", + extension_data.SetKey(kTypeKey, base::Value(TypeToString(extension->GetType()))); - extension_data.SetKey("version", + extension_data.SetKey(kVersionKey, base::Value(extension->GetVersionForDisplay())); data.GetList().push_back(std::move(extension_data)); } + // Aggregate and add the data for the registered event listeners. + AddEventListenerData(extensions::EventRouter::Get(profile_), &data); + std::string json; base::JSONWriter::WriteWithOptions( data, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc index 4b10001..8672961 100644 --- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -32,13 +32,13 @@ const base::ListValue* args) { AllowJavascript(); ShowCrostiniInstallerView(Profile::FromWebUI(web_ui()), - CrostiniUISurface::kSettings); + crostini::CrostiniUISurface::kSettings); } void CrostiniHandler::HandleRequestRemoveCrostini(const base::ListValue* args) { AllowJavascript(); ShowCrostiniUninstallerView(Profile::FromWebUI(web_ui()), - CrostiniUISurface::kSettings); + crostini::CrostiniUISurface::kSettings); } } // namespace settings
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc index 9900d621..e2b4af3 100644 --- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc
@@ -328,7 +328,7 @@ } void StorageHandler::UpdateCrostiniSize() { - if (!IsCrostiniEnabled(profile_)) { + if (!crostini::IsCrostiniEnabled(profile_)) { return; }
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc index 769ed65e..f08a83f9 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -204,7 +204,7 @@ } AddSettingsPageUIHandler( std::make_unique<chromeos::settings::ChangePictureHandler>()); - if (IsCrostiniUIAllowedForProfile(profile)) { + if (crostini::IsCrostiniUIAllowedForProfile(profile)) { AddSettingsPageUIHandler( std::make_unique<chromeos::settings::CrostiniHandler>()); } @@ -323,7 +323,7 @@ ash::stylus_utils::HasInternalStylus()); html_source->AddBoolean("showCrostini", - IsCrostiniUIAllowedForProfile(profile)); + crostini::IsCrostiniUIAllowedForProfile(profile)); // TODO(crbug.com/868747): Show an explanatory message instead of hiding the // storage management info.
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index ab7c907..ace252e 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -359,7 +359,6 @@ "extensions/manifest_handlers/theme_handler.h", "extensions/manifest_handlers/ui_overrides_handler.cc", "extensions/manifest_handlers/ui_overrides_handler.h", - "extensions/mojom/inline_install_traits.h", "extensions/permissions/chrome_api_permissions.cc", "extensions/permissions/chrome_api_permissions.h", "extensions/permissions/chrome_permission_message_provider.cc", @@ -375,7 +374,6 @@ ] deps += [ "//chrome/common/apps/platform_apps" ] public_deps += [ - "//chrome/common/extensions:mojo_bindings", "//chrome/common/extensions/api", "//chrome/common/extensions/api:extensions_features", "//device/usb",
diff --git a/chrome/common/common_message_generator.h b/chrome/common/common_message_generator.h index a27108e3..8039857 100644 --- a/chrome/common/common_message_generator.h +++ b/chrome/common/common_message_generator.h
@@ -44,7 +44,6 @@ #ifndef CHROME_COMMON_EXTENSIONS_CHROME_EXTENSION_MESSAGES_H_ #error "Failed to include chrome/common/extensions/chrome_extension_messages.h" #endif -#include "chrome/common/extensions/mojom/inline_install_traits.h" #endif #if BUILDFLAG(ENABLE_PRINTING)
diff --git a/chrome/common/extensions/BUILD.gn b/chrome/common/extensions/BUILD.gn index 676919f1..7cefd88 100644 --- a/chrome/common/extensions/BUILD.gn +++ b/chrome/common/extensions/BUILD.gn
@@ -4,7 +4,6 @@ import("//build/config/features.gni") import("//extensions/buildflags/buildflags.gni") -import("//mojo/public/tools/bindings/mojom.gni") import("//tools/json_schema_compiler/json_features.gni") assert(enable_extensions) @@ -22,9 +21,3 @@ ":extension_features_unittest", ] } - -mojom("mojo_bindings") { - sources = [ - "mojom/inline_install.mojom", - ] -}
diff --git a/chrome/common/extensions/api/accessibility_private.json b/chrome/common/extensions/api/accessibility_private.json index 40924acb..eec66faa 100644 --- a/chrome/common/extensions/api/accessibility_private.json +++ b/chrome/common/extensions/api/accessibility_private.json
@@ -225,6 +225,13 @@ } ], "platforms": ["chromeos"] + }, + { + "name": "toggleDictation", + "type": "function", + "description": "Toggles dictation between active and inactive states.", + "parameters": [], + "platforms": ["chromeos"] } ], "events": [
diff --git a/chrome/common/extensions/mojom/OWNERS b/chrome/common/extensions/mojom/OWNERS deleted file mode 100644 index 229dacdd..0000000 --- a/chrome/common/extensions/mojom/OWNERS +++ /dev/null
@@ -1,4 +0,0 @@ -per-file *.mojom=set noparent -per-file *.mojom=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/chrome/common/extensions/mojom/inline_install.mojom b/chrome/common/extensions/mojom/inline_install.mojom deleted file mode 100644 index e298424..0000000 --- a/chrome/common/extensions/mojom/inline_install.mojom +++ /dev/null
@@ -1,31 +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 extensions.mojom; - -[Native] -enum WebstoreInstallResult; - -[Native] -enum WebstoreInstallStage; - -interface InlineInstallProgressListener { - // Notifies the renderer when install stage updates were requested for an - // inline install. - InlineInstallStageChanged(WebstoreInstallStage stage); - - // Notifies the renderer when download progress updates were requested for an - // inline install. - InlineInstallDownloadProgress(int32 percent_downloaded); -}; - -interface InlineInstaller { - // Sent by the renderer to implement chrome.webstore.install() and notifies - // the renderer once the installation is complete. - DoInlineInstall(string webstore_item_id, int32 listeners_mask, - InlineInstallProgressListener install_progress_listener) => - (bool success, string error, WebstoreInstallResult result); -}; - -
diff --git a/chrome/common/extensions/mojom/inline_install.typemap b/chrome/common/extensions/mojom/inline_install.typemap deleted file mode 100644 index 3a08ce5..0000000 --- a/chrome/common/extensions/mojom/inline_install.typemap +++ /dev/null
@@ -1,14 +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 = "//chrome/common/extensions/mojom/inline_install.mojom" -public_headers = [ - "//chrome/common/extensions/api/webstore/webstore_api_constants.h", - "//chrome/common/extensions/webstore_install_result.h", -] -traits_headers = [ "//chrome/common/extensions/mojom/inline_install_traits.h" ] -type_mappings = [ - "extensions.mojom.WebstoreInstallStage=::extensions::api::webstore::InstallStage", - "extensions.mojom.WebstoreInstallResult=::extensions::webstore_install::Result", -]
diff --git a/chrome/common/extensions/mojom/inline_install_traits.h b/chrome/common/extensions/mojom/inline_install_traits.h deleted file mode 100644 index 005d17d..0000000 --- a/chrome/common/extensions/mojom/inline_install_traits.h +++ /dev/null
@@ -1,16 +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. - -// Multiply-included file, hence no include guard. - -#include "chrome/common/extensions/api/webstore/webstore_api_constants.h" -#include "chrome/common/extensions/webstore_install_result.h" -#include "ipc/ipc_message_macros.h" - -IPC_ENUM_TRAITS_MAX_VALUE( - extensions::api::webstore::InstallStage, - extensions::api::webstore::InstallStage::INSTALL_STAGE_INSTALLING) -IPC_ENUM_TRAITS_MAX_VALUE(extensions::webstore_install::Result, - extensions::webstore_install::RESULT_LAST) -
diff --git a/chrome/common/extensions/typemaps.gni b/chrome/common/extensions/typemaps.gni deleted file mode 100644 index 2d63179..0000000 --- a/chrome/common/extensions/typemaps.gni +++ /dev/null
@@ -1,5 +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. - -typemaps = [ "//chrome/common/extensions/mojom/inline_install.typemap" ]
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn index 5f1fed1..7866209 100644 --- a/chrome/renderer/BUILD.gn +++ b/chrome/renderer/BUILD.gn
@@ -309,8 +309,6 @@ "extensions/sync_file_system_custom_bindings.h", "extensions/tabs_hooks_delegate.cc", "extensions/tabs_hooks_delegate.h", - "extensions/webstore_bindings.cc", - "extensions/webstore_bindings.h", "media/cast_ipc_dispatcher.cc", "media/cast_ipc_dispatcher.h", "media/cast_receiver_audio_valve.cc",
diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc index bd1695f..a8c8257 100644 --- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc +++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc
@@ -25,7 +25,6 @@ #include "chrome/renderer/extensions/page_capture_custom_bindings.h" #include "chrome/renderer/extensions/sync_file_system_custom_bindings.h" #include "chrome/renderer/extensions/tabs_hooks_delegate.h" -#include "chrome/renderer/extensions/webstore_bindings.h" #include "components/version_info/version_info.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_thread.h" @@ -132,9 +131,6 @@ "page_capture", std::unique_ptr<NativeHandler>( new extensions::PageCaptureCustomBindings(context))); module_system->RegisterNativeHandler( - "webstore", std::unique_ptr<NativeHandler>( - new extensions::WebstoreBindings(context))); - module_system->RegisterNativeHandler( "cast_streaming_natives", std::make_unique<extensions::CastStreamingNativeHandler>( context, bindings_system));
diff --git a/chrome/renderer/extensions/webstore_bindings.cc b/chrome/renderer/extensions/webstore_bindings.cc deleted file mode 100644 index bbbda3c..0000000 --- a/chrome/renderer/extensions/webstore_bindings.cc +++ /dev/null
@@ -1,267 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/renderer/extensions/webstore_bindings.h" - -#include <stdint.h> - -#include "base/macros.h" -#include "base/strings/string_util.h" -#include "chrome/common/extensions/api/webstore/webstore_api_constants.h" -#include "components/crx_file/id_util.h" -#include "content/public/renderer/render_frame.h" -#include "extensions/common/extension.h" -#include "extensions/common/extension_urls.h" -#include "extensions/renderer/script_context.h" -#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" -#include "third_party/blink/public/web/web_document.h" -#include "third_party/blink/public/web/web_element.h" -#include "third_party/blink/public/web/web_local_frame.h" -#include "third_party/blink/public/web/web_node.h" -#include "third_party/blink/public/web/web_user_gesture_indicator.h" -#include "url/gurl.h" -#include "v8/include/v8.h" - -using blink::WebDocument; -using blink::WebElement; -using blink::WebNode; -using blink::WebUserGestureIndicator; - -namespace extensions { - -namespace { - -const char kWebstoreLinkRelation[] = "chrome-webstore-item"; - -const char kNotInTopFrameError[] = - "Chrome Web Store installations can only be started by the top frame."; -const char kNotUserGestureError[] = - "Chrome Web Store installations can only be initated by a user gesture."; -const char kNoWebstoreItemLinkFoundError[] = - "No Chrome Web Store item link found."; -const char kInvalidWebstoreItemUrlError[] = - "Invalid Chrome Web Store item URL."; - -// chrome.webstore.install() calls generate an install ID so that the install's -// callbacks may be fired when the browser notifies us of install completion -// (successful or not) via |InlineInstallResponse|. -int g_next_install_id = 0; - -} // anonymous namespace - -WebstoreBindings::WebstoreBindings(ScriptContext* context) - : ObjectBackedNativeHandler(context) { - content::RenderFrame* render_frame = context->GetRenderFrame(); - if (render_frame) - render_frame->GetRemoteAssociatedInterfaces()->GetInterface( - &inline_installer_); -} - -WebstoreBindings::~WebstoreBindings() {} - -void WebstoreBindings::AddRoutes() { - RouteHandlerFunction( - "Install", "webstore", - base::Bind(&WebstoreBindings::Install, base::Unretained(this))); -} - -void WebstoreBindings::InlineInstallResponse(int install_id, - bool success, - const std::string& error, - webstore_install::Result result) { - v8::Isolate* isolate = context()->isolate(); - v8::HandleScope handle_scope(isolate); - v8::Context::Scope context_scope(context()->v8_context()); - v8::Local<v8::Value> argv[] = { - v8::Integer::New(isolate, install_id), v8::Boolean::New(isolate, success), - v8::String::NewFromUtf8(isolate, error.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked(), - v8::String::NewFromUtf8( - isolate, api::webstore::kInstallResultCodes[static_cast<int>(result)], - v8::NewStringType::kNormal) - .ToLocalChecked()}; - context()->module_system()->CallModuleMethodSafe( - "webstore", "onInstallResponse", arraysize(argv), argv); -} - -void WebstoreBindings::InlineInstallStageChanged( - api::webstore::InstallStage stage) { - const char* stage_string = nullptr; - switch (stage) { - case api::webstore::INSTALL_STAGE_DOWNLOADING: - stage_string = api::webstore::kInstallStageDownloading; - break; - case api::webstore::INSTALL_STAGE_INSTALLING: - stage_string = api::webstore::kInstallStageInstalling; - break; - } - v8::Isolate* isolate = context()->isolate(); - v8::HandleScope handle_scope(isolate); - v8::Context::Scope context_scope(context()->v8_context()); - v8::Local<v8::Value> argv[] = { - v8::String::NewFromUtf8(isolate, stage_string, v8::NewStringType::kNormal) - .ToLocalChecked()}; - context()->module_system()->CallModuleMethodSafe( - "webstore", "onInstallStageChanged", arraysize(argv), argv); -} - -void WebstoreBindings::InlineInstallDownloadProgress(int percent_downloaded) { - v8::Isolate* isolate = context()->isolate(); - v8::HandleScope handle_scope(isolate); - v8::Context::Scope context_scope(context()->v8_context()); - v8::Local<v8::Value> argv[] = { - v8::Number::New(isolate, percent_downloaded / 100.0)}; - context()->module_system()->CallModuleMethodSafe( - "webstore", "onDownloadProgress", arraysize(argv), argv); -} - -void WebstoreBindings::Install( - const v8::FunctionCallbackInfo<v8::Value>& args) { - content::RenderFrame* render_frame = context()->GetRenderFrame(); - if (!render_frame) - return; - - // The first two arguments indicate whether or not there are install stage - // or download progress listeners. - int listener_mask = 0; - CHECK(args[0]->IsBoolean()); - if (args[0].As<v8::Boolean>()->Value()) - listener_mask |= api::webstore::INSTALL_STAGE_LISTENER; - CHECK(args[1]->IsBoolean()); - if (args[1].As<v8::Boolean>()->Value()) - listener_mask |= api::webstore::DOWNLOAD_PROGRESS_LISTENER; - - std::string preferred_store_link_url; - if (!args[2]->IsUndefined()) { - CHECK(args[2]->IsString()); - preferred_store_link_url = - std::string(*v8::String::Utf8Value(args.GetIsolate(), args[2])); - } - - std::string webstore_item_id; - std::string error; - blink::WebLocalFrame* frame = context()->web_frame(); - - if (!GetWebstoreItemIdFromFrame( - frame, preferred_store_link_url, &webstore_item_id, &error)) { - args.GetIsolate()->ThrowException( - v8::String::NewFromUtf8(args.GetIsolate(), error.c_str(), - v8::NewStringType::kNormal) - .ToLocalChecked()); - return; - } - - int install_id = g_next_install_id++; - - mojom::InlineInstallProgressListenerPtr install_progress_listener; - install_progress_listener_bindings_.AddBinding( - this, mojo::MakeRequest(&install_progress_listener)); - - inline_installer_->DoInlineInstall( - webstore_item_id, listener_mask, std::move(install_progress_listener), - base::Bind(&WebstoreBindings::InlineInstallResponse, - base::Unretained(this), install_id)); - args.GetReturnValue().Set(static_cast<int32_t>(install_id)); -} - -// static -bool WebstoreBindings::GetWebstoreItemIdFromFrame( - blink::WebLocalFrame* frame, - const std::string& preferred_store_link_url, - std::string* webstore_item_id, - std::string* error) { - if (frame != frame->Top()) { - *error = kNotInTopFrameError; - return false; - } - - if (!WebUserGestureIndicator::IsProcessingUserGesture(frame)) { - *error = kNotUserGestureError; - return false; - } - - WebDocument document = frame->GetDocument(); - if (document.IsNull()) { - *error = kNoWebstoreItemLinkFoundError; - return false; - } - - WebElement head = document.Head(); - if (head.IsNull()) { - *error = kNoWebstoreItemLinkFoundError; - return false; - } - - GURL webstore_base_url = - GURL(extension_urls::GetWebstoreItemDetailURLPrefix()); - for (WebNode child = head.FirstChild(); !child.IsNull(); - child = child.NextSibling()) { - if (!child.IsElementNode()) - continue; - WebElement elem = child.To<WebElement>(); - - if (!elem.HasHTMLTagName("link") || !elem.HasAttribute("rel") || - !elem.HasAttribute("href")) - continue; - - std::string rel = elem.GetAttribute("rel").Utf8(); - if (!base::LowerCaseEqualsASCII(rel, kWebstoreLinkRelation)) - continue; - - std::string webstore_url_string(elem.GetAttribute("href").Utf8()); - - if (!preferred_store_link_url.empty() && - preferred_store_link_url != webstore_url_string) { - continue; - } - - GURL webstore_url = GURL(webstore_url_string); - if (!webstore_url.is_valid()) { - *error = kInvalidWebstoreItemUrlError; - return false; - } - - if (webstore_url.scheme() != webstore_base_url.scheme() || - webstore_url.host() != webstore_base_url.host() || - !base::StartsWith(webstore_url.path(), webstore_base_url.path(), - base::CompareCase::SENSITIVE)) { - *error = kInvalidWebstoreItemUrlError; - return false; - } - - std::string candidate_webstore_item_id = webstore_url.path().substr( - webstore_base_url.path().length()); - if (!crx_file::id_util::IdIsValid(candidate_webstore_item_id)) { - *error = kInvalidWebstoreItemUrlError; - return false; - } - - std::string reconstructed_webstore_item_url_string = - extension_urls::GetWebstoreItemDetailURLPrefix() + - candidate_webstore_item_id; - if (reconstructed_webstore_item_url_string != webstore_url_string) { - *error = kInvalidWebstoreItemUrlError; - return false; - } - - *webstore_item_id = candidate_webstore_item_id; - return true; - } - - *error = kNoWebstoreItemLinkFoundError; - return false; -} - -void WebstoreBindings::Invalidate() { - // We should close all mojo pipes when we invalidate the WebstoreBindings - // object and before its associated v8::context is destroyed. This is to - // ensure there are no mojo calls that try to access the v8::context after its - // destruction. - inline_installer_.reset(); - install_progress_listener_bindings_.CloseAllBindings(); - ObjectBackedNativeHandler::Invalidate(); -} - -} // namespace extensions
diff --git a/chrome/renderer/extensions/webstore_bindings.h b/chrome/renderer/extensions/webstore_bindings.h deleted file mode 100644 index 398dd01a..0000000 --- a/chrome/renderer/extensions/webstore_bindings.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_RENDERER_EXTENSIONS_WEBSTORE_BINDINGS_H_ -#define CHROME_RENDERER_EXTENSIONS_WEBSTORE_BINDINGS_H_ - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "chrome/common/extensions/mojom/inline_install.mojom.h" -#include "chrome/common/extensions/webstore_install_result.h" -#include "extensions/renderer/object_backed_native_handler.h" -#include "mojo/public/cpp/bindings/binding_set.h" -#include "v8/include/v8.h" - -namespace blink { -class WebLocalFrame; -} - -namespace extensions { -class ScriptContext; - -// A V8 extension that creates an object at window.chrome.webstore. This object -// allows JavaScript to initiate inline installs of apps that are listed in the -// Chrome Web Store (CWS). -class WebstoreBindings : public ObjectBackedNativeHandler, - public mojom::InlineInstallProgressListener { - public: - explicit WebstoreBindings(ScriptContext* context); - ~WebstoreBindings() override; - - // ObjectBackedNativeHandler: - void AddRoutes() override; - - // mojom::InlineInstallProgressListener: - void InlineInstallStageChanged(api::webstore::InstallStage stage) override; - void InlineInstallDownloadProgress(int percent_downloaded) override; - - private: - void InlineInstallResponse(int install_id, - bool success, - const std::string& error, - webstore_install::Result result); - void Install(const v8::FunctionCallbackInfo<v8::Value>& args); - - // Extracts a Web Store item ID from a <link rel="chrome-webstore-item" - // href="https://chrome.google.com/webstore/detail/id"> node found in the - // frame. On success, true will be returned and the |webstore_item_id| - // parameter will be populated with the ID. On failure, false will be returned - // and |error| will be populated with the error. - static bool GetWebstoreItemIdFromFrame( - blink::WebLocalFrame* frame, - const std::string& preferred_store_link_url, - std::string* webstore_item_id, - std::string* error); - - // ObjectBackedNativeHandler: - void Invalidate() override; - - mojom::InlineInstallerAssociatedPtr inline_installer_; - - mojo::BindingSet<mojom::InlineInstallProgressListener> - install_progress_listener_bindings_; - - DISALLOW_COPY_AND_ASSIGN(WebstoreBindings); -}; - -} // namespace extensions - -#endif // CHROME_RENDERER_EXTENSIONS_WEBSTORE_BINDINGS_H_
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 5580282..3d223a2 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1410,12 +1410,10 @@ "../browser/extensions/wake_event_page_apitest.cc", "../browser/extensions/wasm_app_browsertest.cc", "../browser/extensions/web_contents_browsertest.cc", - "../browser/extensions/webstore_inline_installer_browsertest.cc", "../browser/extensions/webstore_installer_browsertest.cc", "../browser/extensions/webstore_installer_test.cc", "../browser/extensions/webstore_installer_test.h", "../browser/extensions/webstore_reinstaller_browsertest.cc", - "../browser/extensions/webstore_startup_installer_browsertest.cc", "../browser/extensions/window_open_apitest.cc", "../browser/extensions/worker_apitest.cc", "../browser/notifications/notification_permission_context_apitest.cc", @@ -3401,7 +3399,6 @@ "../browser/sync/sync_error_notifier_ash_unittest.cc", "../browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc", "../browser/ui/ash/accessibility/ax_tree_source_aura_unittest.cc", - "../browser/ui/ash/browser_image_registrar_unittest.cc", "../browser/ui/ash/chrome_keyboard_ui_unittest.cc", "../browser/ui/ash/chrome_keyboard_web_contents_unittest.cc", "../browser/ui/ash/ime_controller_client_unittest.cc", @@ -3603,7 +3600,6 @@ "../browser/extensions/extension_prefs_unittest.cc", "../browser/extensions/extension_prefs_unittest.h", "../browser/extensions/extension_protocols_unittest.cc", - "../browser/extensions/extension_reenabler_unittest.cc", "../browser/extensions/extension_service_sync_unittest.cc", "../browser/extensions/extension_service_test_base.cc", "../browser/extensions/extension_service_test_base.h", @@ -3646,7 +3642,6 @@ "../browser/extensions/updater/extension_updater_unittest.cc", "../browser/extensions/user_script_listener_unittest.cc", "../browser/extensions/warning_badge_service_unittest.cc", - "../browser/extensions/webstore_inline_installer_unittest.cc", "../browser/extensions/webstore_installer_unittest.cc", "../browser/extensions/zipfile_installer_unittest.cc", "../browser/media/cast_transport_host_filter_unittest.cc",
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index 1d914c7..1da5c0c 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -108,8 +108,6 @@ ] _VERSION_SPECIFIC_FILTER['69'] = [ - # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945 - 'ChromeDriverTest.testWindowFullScreen', # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2515 'HeadlessInvalidCertificateTest.*', # Feature not yet supported in this version @@ -117,8 +115,6 @@ ] _VERSION_SPECIFIC_FILTER['68'] = [ - # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945 - 'ChromeDriverTest.testWindowFullScreen', # Feature not yet supported in this version 'ChromeDriverTest.testGenerateTestReport', ] @@ -141,6 +137,16 @@ 'ChromeDriverTest.testWindowMaximize', ] +_OS_VERSION_SPECIFIC_FILTER = {} +_OS_VERSION_SPECIFIC_FILTER['mac', '68'] = [ + # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945 + 'ChromeDriverTest.testWindowFullScreen', +] +_OS_VERSION_SPECIFIC_FILTER['mac', '69'] = [ + # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945 + 'ChromeDriverTest.testWindowFullScreen', +] + _DESKTOP_NEGATIVE_FILTER = [ # Desktop doesn't support touch (without --touch-events). 'ChromeDriverTest.testTouchSingleTapElement', @@ -190,6 +196,8 @@ def _GetDesktopNegativeFilter(version_name): filter = _NEGATIVE_FILTER + _DESKTOP_NEGATIVE_FILTER os = util.GetPlatformName() + if (os, version_name) in _OS_VERSION_SPECIFIC_FILTER: + filter += _OS_VERSION_SPECIFIC_FILTER[os, version_name] if os in _OS_SPECIFIC_FILTER: filter += _OS_SPECIFIC_FILTER[os] if version_name in _VERSION_SPECIFIC_FILTER:
diff --git a/chrome/test/data/webui/extensions/options_dialog_test.js b/chrome/test/data/webui/extensions/options_dialog_test.js index a61a6bff..81ebdd1 100644 --- a/chrome/test/data/webui/extensions/options_dialog_test.js +++ b/chrome/test/data/webui/extensions/options_dialog_test.js
@@ -42,19 +42,23 @@ optionsDialog.show(data); return test_util.eventToPromise('cr-dialog-open', optionsDialog) .then(function() { - assertTrue(isDialogVisible()); + // The dialog size is set asynchronously (see onpreferredsizechanged + // in options_dialog.js) so wait one frame. + requestAnimationFrame(function() { + assertTrue(isDialogVisible()); - const dialogElement = optionsDialog.$.dialog.getNative(); - const rect = dialogElement.getBoundingClientRect(); - assertGE(rect.width, extensions.OptionsDialogMinWidth); - assertLE(rect.height, extensions.OptionsDialogMaxHeight); - // This is the header height with default font size. - assertGE(rect.height, 68); + const dialogElement = optionsDialog.$.dialog.getNative(); + const rect = dialogElement.getBoundingClientRect(); + assertGE(rect.width, extensions.OptionsDialogMinWidth); + assertLE(rect.height, extensions.OptionsDialogMaxHeight); + // This is the header height with default font size. + assertGE(rect.height, 68); - assertEquals( - data.name, - assert(optionsDialog.$$('#icon-and-name-wrapper span')) - .textContent.trim()); + assertEquals( + data.name, + assert(optionsDialog.$$('#icon-and-name-wrapper span')) + .textContent.trim()); + }); }); }); });
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js index acf839b..d0d7e78a 100644 --- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js +++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.js
@@ -6,6 +6,7 @@ /** @enum {string} */ const TestNames = { FocusSearchBox: 'focus search box', + EscapeSearchBox: 'escape search box', }; const suiteName = 'DestinationDialogInteractiveTest'; @@ -59,6 +60,56 @@ dialog.show(); return whenFocusDone; }); + + // Tests that pressing the escape key while the search box is focused + // closes the dialog if and only if the query is empty. + test(assert(TestNames.EscapeSearchBox), function() { + const searchInput = dialog.$.searchBox.getSearchInput(); + assertTrue(!!searchInput); + const whenFocusDone = test_util.eventToPromise('focus', searchInput); + destinationStore.startLoadAllDestinations(); + dialog.show(); + return whenFocusDone + .then(() => { + assertTrue(dialog.$.dialog.open); + + // Put something in the search box. + const whenSearchChanged = + test_util.eventToPromise('search-changed', dialog.$.searchBox); + dialog.$.searchBox.setValue('query'); + return whenSearchChanged; + }) + .then(() => { + assertEquals('query', searchInput.value); + + // Simulate escape + const whenKeyDown = test_util.eventToPromise('keydown', dialog); + MockInteractions.keyDownOn(searchInput, 19, [], 'Escape'); + return whenKeyDown; + }) + .then(() => { + // Dialog should still be open. + assertTrue(dialog.$.dialog.open); + + // Clear the search box. + const whenSearchChanged = + test_util.eventToPromise('search-changed', dialog.$.searchBox); + dialog.$.searchBox.setValue(''); + return whenSearchChanged; + }) + .then(() => { + assertEquals('', searchInput.value); + + // Simulate escape + const whenKeyDown = test_util.eventToPromise('keydown', dialog); + MockInteractions.keyDownOn(searchInput, 19, [], 'Escape'); + return whenKeyDown; + }) + .then(() => { + // Dialog is closed. + assertFalse(dialog.$.dialog.open); + }); + }); }); return {
diff --git a/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.js b/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.js new file mode 100644 index 0000000..e0212ce --- /dev/null +++ b/chrome/test/data/webui/print_preview/number_settings_section_interactive_test.js
@@ -0,0 +1,73 @@ +// 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. + +cr.define('number_settings_section_interactive_test', function() { + /** @enum {string} */ + const TestNames = { + BlurResetsEmptyInput: 'blur resets empty input', + }; + + const suiteName = 'NumberSettingsSectionInteractiveTest'; + suite(suiteName, function() { + /** @type {?PrintPreviewNumberSettingsSectionElement} */ + let numberSettings = null; + + /** @override */ + setup(function() { + PolymerTest.clearBody(); + + document.body.innerHTML = ` + <print-preview-number-settings-section id="numberSettings" + min-value="1" max-value="100" default-value="50" + current-value="10" hint-message="incorrect value entered" + input-valid> + </print-preview-number-settings-section>`; + numberSettings = document.querySelector('#numberSettings'); + }); + + // Verifies that blurring the input will reset it to the default if it is + // empty, but not if it contains an invalid value. + test(assert(TestNames.BlurResetsEmptyInput), function() { + // Initial value is 10. + const crInput = numberSettings.getInput(); + const input = crInput.inputElement; + assertEquals('10', input.value); + + // Set something invalid in the input. + const whenFocused = test_util.eventToPromise('focus', input); + input.focus(); + print_preview_test_utils.triggerInputEvent(input, '0'); + return test_util.eventToPromise('input-change', numberSettings) + .then(() => { + assertEquals('0', input.value); + assertTrue(crInput.invalid); + + // Blurring the input does not clear it or clear the error if there + // is an explicit invalid value. + input.blur(); + assertEquals('0', input.value); + assertTrue(crInput.invalid); + + // Clear the input. + input.focus(); + print_preview_test_utils.triggerInputEvent(input, ''); + return test_util.eventToPromise('input-change', numberSettings); + }) + .then(() => { + assertEquals('', input.value); + assertFalse(crInput.invalid); + + // Blurring the input clears it to the default when it is empty. + input.blur(); + assertEquals('50', input.value); + assertFalse(crInput.invalid); + }); + }); + }); + + return { + suiteName: suiteName, + TestNames: TestNames, + }; +});
diff --git a/chrome/test/data/webui/print_preview/number_settings_section_test.js b/chrome/test/data/webui/print_preview/number_settings_section_test.js index 52a3461..12c2db9 100644 --- a/chrome/test/data/webui/print_preview/number_settings_section_test.js +++ b/chrome/test/data/webui/print_preview/number_settings_section_test.js
@@ -20,8 +20,8 @@ document.body.innerHTML = ` <div id="parentElement"> <print-preview-number-settings-section id="numberSettings" - disabled="false" min-value="1" max-value="100" default-value="50" - hint-message="incorrect value entered" input-valid="true"> + min-value="1" max-value="100" default-value="50" + hint-message="incorrect value entered" input-valid> </print-preview-number-settings-section> </div>`; parentElement = document.querySelector('#parentElement'); @@ -44,19 +44,31 @@ return whenKeyDown; }; - return sendKeyDownAndReturnPromise(69, 'e').then(e => { - assertTrue(e.defaultPrevented); - return sendKeyDownAndReturnPromise(110, '.'); - }).then(e => { - assertTrue(e.defaultPrevented); - return sendKeyDownAndReturnPromise(109, '-'); - }).then(e => { - assertTrue(e.defaultPrevented); - // Try a valid key. - return sendKeyDownAndReturnPromise(49, '1'); - }).then(e => { - assertFalse(e.defaultPrevented); - }); + return sendKeyDownAndReturnPromise(69, 'e') + .then(e => { + assertTrue(e.defaultPrevented); + return sendKeyDownAndReturnPromise(110, '.'); + }) + .then(e => { + assertTrue(e.defaultPrevented); + return sendKeyDownAndReturnPromise(109, '-'); + }) + .then(e => { + assertTrue(e.defaultPrevented); + return sendKeyDownAndReturnPromise(69, 'E'); + }) + .then(e => { + assertTrue(e.defaultPrevented); + return sendKeyDownAndReturnPromise(187, '+'); + }) + .then(e => { + assertTrue(e.defaultPrevented); + // Try a valid key. + return sendKeyDownAndReturnPromise(49, '1'); + }) + .then(e => { + assertFalse(e.defaultPrevented); + }); }); });
diff --git a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js index 7136481..baffb59 100644 --- a/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js +++ b/chrome/test/data/webui/print_preview/print_preview_interactive_ui_tests.js
@@ -100,6 +100,13 @@ destination_dialog_interactive_test.TestNames.FocusSearchBox); }); +TEST_F( + 'PrintPreviewDestinationDialogInteractiveTest', 'EscapeSearchBox', + function() { + this.runMochaTest( + destination_dialog_interactive_test.TestNames.EscapeSearchBox); + }); + PrintPreviewPagesSettingsTest = class extends PrintPreviewInteractiveUITest { /** @override */ get browsePreload() { @@ -140,3 +147,32 @@ TEST_F('PrintPreviewPagesSettingsTest', 'TabOrder', function() { this.runMochaTest(pages_settings_test.TestNames.TabOrder); }); + +PrintPreviewNumberSettingsSectionInteractiveTest = + class extends PrintPreviewInteractiveUITest { + /** @override */ + get browsePreload() { + return 'chrome://print/new/number_settings_section.html'; + } + + /** @override */ + get extraLibraries() { + return super.extraLibraries.concat([ + '../settings/test_util.js', + 'print_preview_test_utils.js', + 'number_settings_section_interactive_test.js', + ]); + } + + /** @override */ + get suiteName() { + return number_settings_section_interactive_test.suiteName; + } +}; + +TEST_F( + 'PrintPreviewNumberSettingsSectionInteractiveTest', 'BlurResetsEmptyInput', + function() { + this.runMochaTest(number_settings_section_interactive_test.TestNames + .BlurResetsEmptyInput); + });
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js index 6d8e5a3..84f7bf1 100644 --- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js +++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -281,6 +281,16 @@ }; } + /** + * @param {!HTMLInputElement} element + * @param {!string} input The value to set for the input element. + */ + function triggerInputEvent(element, input) { + element.value = input; + element.dispatchEvent( + new CustomEvent('input', {composed: true, bubbles: true})); + } + return { getDefaultInitialSettings: getDefaultInitialSettings, getCddTemplate: getCddTemplate, @@ -293,5 +303,6 @@ getMediaSizeCapabilityWithCustomNames: getMediaSizeCapabilityWithCustomNames, getPdfPrinter: getPdfPrinter, + triggerInputEvent: triggerInputEvent, }; });
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js index dc8332c4..42346ed 100644 --- a/chrome/test/data/webui/print_preview/settings_section_test.js +++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -106,16 +106,6 @@ moreSettingsElement.$.label.click(); } - /** - * @param {!HTMLInputElement} element - * @param {!string} input The value to set for the input element. - */ - function triggerInputEvent(element, input) { - element.value = input; - element.dispatchEvent( - new CustomEvent('input', {composed: true, bubbles: true})); - } - test(assert(TestNames.Copies), function() { const copiesElement = page.$$('print-preview-copies-settings'); assertFalse(copiesElement.hidden); @@ -587,7 +577,7 @@ // platforms. pagesElement.set('optionSelected_', pagesElement.pagesValueEnum_.CUSTOM); - triggerInputEvent(pagesInput, '1-2'); + print_preview_test_utils.triggerInputEvent(pagesInput, '1-2'); return test_util.eventToPromise('input-change', pagesElement) .then(function() { validateInputState(false, '1-2', true); @@ -598,7 +588,7 @@ assertTrue(page.settings.pages.valid); // Select pages 1 and 3 - triggerInputEvent(pagesInput, '1, 3'); + print_preview_test_utils.triggerInputEvent(pagesInput, '1, 3'); return test_util.eventToPromise('input-change', pagesElement); }) .then(function() { @@ -612,7 +602,7 @@ assertTrue(page.settings.pages.valid); // Enter an out of bounds value. - triggerInputEvent(pagesInput, '5'); + print_preview_test_utils.triggerInputEvent(pagesInput, '5'); return test_util.eventToPromise('input-change', pagesElement); }) .then(function() { @@ -635,7 +625,7 @@ assertEquals(1, page.settings.copies.value); // Change to 2 - triggerInputEvent(copiesInput, '2'); + print_preview_test_utils.triggerInputEvent(copiesInput, '2'); return test_util.eventToPromise('input-change', copiesElement) .then(function() { assertEquals(2, page.settings.copies.value); @@ -650,6 +640,37 @@ assertFalse(collateInput.checked); collateInput.dispatchEvent(new CustomEvent('change')); assertFalse(page.settings.collate.value); + + // Set an empty value. + print_preview_test_utils.triggerInputEvent(copiesInput, ''); + return test_util.eventToPromise('input-change', copiesElement); + }) + .then(function() { + // Collate should be hidden now, but no update to the backing value + // occurs. + assertTrue(copiesElement.$$('.checkbox').hidden); + assertTrue(page.settings.copies.valid); + assertEquals(2, page.settings.copies.value); + + // If the field is blurred, it will be reset to the default by the + // number-settings-section. Simulate this ocurring. + const numberSettingsSection = + copiesElement.$$('print-preview-number-settings-section'); + numberSettingsSection.$.userValue.value = '1'; + numberSettingsSection.currentValue = '1'; + assertTrue(page.settings.copies.valid); + assertEquals(1, page.settings.copies.value); + + // Enter an invalid value. + print_preview_test_utils.triggerInputEvent(copiesInput, '0'); + return test_util.eventToPromise('input-change', copiesElement); + }) + .then(function() { + // Collate should be hidden. Value is not updated to the invalid + // number. Setting is marked invalid. + assertTrue(copiesElement.$$('.checkbox').hidden); + assertFalse(page.settings.copies.valid); + assertEquals(1, page.settings.copies.value); }); }); @@ -884,7 +905,7 @@ validateScalingState('100', true, false, false); // Change to 105 - triggerInputEvent(scalingInput, '105'); + print_preview_test_utils.triggerInputEvent(scalingInput, '105'); return test_util.eventToPromise('input-change', scalingElement) .then(function() { validateScalingState('105', true, false, false); @@ -902,7 +923,7 @@ // Set scaling. Should uncheck fit to page and set the settings for // scaling and fit to page. - triggerInputEvent(scalingInput, '95'); + print_preview_test_utils.triggerInputEvent(scalingInput, '95'); return test_util.eventToPromise('input-change', scalingElement); }) .then(function() { @@ -910,7 +931,7 @@ // Set scaling to something invalid. Should change setting validity // but not value. - triggerInputEvent(scalingInput, '5'); + print_preview_test_utils.triggerInputEvent(scalingInput, '5'); return test_util.eventToPromise('input-change', scalingElement); }) .then(function() { @@ -951,7 +972,7 @@ // change the stored value of scaling or fit to page, to avoid an // unnecessary preview regeneration, but should display fit to page // as unchecked. - triggerInputEvent(scalingInput, '9'); + print_preview_test_utils.triggerInputEvent(scalingInput, '9'); return test_util.eventToPromise('input-change', scalingElement); }) .then(function() { @@ -960,7 +981,7 @@ // Enter a blank value in the scaling field. This should not // change the stored value of scaling or fit to page, to avoid an // unnecessary preview regeneration. - triggerInputEvent(scalingInput, ''); + print_preview_test_utils.triggerInputEvent(scalingInput, ''); return test_util.eventToPromise('input-change', scalingElement); }) .then(function() { @@ -968,7 +989,7 @@ // Entering something valid unsets fit to page and sets scaling // valid to true. - triggerInputEvent(scalingInput, '90'); + print_preview_test_utils.triggerInputEvent(scalingInput, '90'); return test_util.eventToPromise('input-change', scalingElement); }) .then(function() {
diff --git a/chrome/utility/importer/nss_decryptor_win.cc b/chrome/utility/importer/nss_decryptor_win.cc index 587d3fe9..9b03397 100644 --- a/chrome/utility/importer/nss_decryptor_win.cc +++ b/chrome/utility/importer/nss_decryptor_win.cc
@@ -15,7 +15,7 @@ // effects of a previous SetDllDirectory call. class SetDllDirectoryCaller { public: - explicit SetDllDirectoryCaller() : func_(NULL) { } + SetDllDirectoryCaller() : func_(NULL) {} ~SetDllDirectoryCaller() { if (func_) @@ -97,13 +97,18 @@ } NSSDecryptor::NSSDecryptor() - : NSS_Init(NULL), NSS_Shutdown(NULL), PK11_GetInternalKeySlot(NULL), - PK11_CheckUserPassword(NULL), PK11_FreeSlot(NULL), - PK11_Authenticate(NULL), PK11SDR_Decrypt(NULL), SECITEM_FreeItem(NULL), - PL_ArenaFinish(NULL), PR_Cleanup(NULL), - nss3_dll_(NULL), softokn3_dll_(NULL), - is_nss_initialized_(false) { -} + : NSS_Init(NULL), + NSS_Shutdown(NULL), + PK11_GetInternalKeySlot(NULL), + PK11_FreeSlot(NULL), + PK11_Authenticate(NULL), + PK11SDR_Decrypt(NULL), + SECITEM_FreeItem(NULL), + PL_ArenaFinish(NULL), + PR_Cleanup(NULL), + nss3_dll_(NULL), + softokn3_dll_(NULL), + is_nss_initialized_(false) {} NSSDecryptor::~NSSDecryptor() { Free();
diff --git a/chrome/utility/importer/nss_decryptor_win.h b/chrome/utility/importer/nss_decryptor_win.h index edcf46c2..79bf16d3 100644 --- a/chrome/utility/importer/nss_decryptor_win.h +++ b/chrome/utility/importer/nss_decryptor_win.h
@@ -166,7 +166,6 @@ NSSInitFunc NSS_Init; NSSShutdownFunc NSS_Shutdown; PK11GetInternalKeySlotFunc PK11_GetInternalKeySlot; - PK11CheckUserPasswordFunc PK11_CheckUserPassword; PK11FreeSlotFunc PK11_FreeSlot; PK11AuthenticateFunc PK11_Authenticate; PK11SDRDecryptFunc PK11SDR_Decrypt;
diff --git a/chromecast/browser/cast_media_blocker_unittest.cc b/chromecast/browser/cast_media_blocker_unittest.cc index dc4eb73d..4bb1a53 100644 --- a/chromecast/browser/cast_media_blocker_unittest.cc +++ b/chromecast/browser/cast_media_blocker_unittest.cc
@@ -39,7 +39,11 @@ MOCK_METHOD1(SetDuckingVolumeMultiplier, void(double)); MOCK_METHOD1(DidReceiveAction, void(blink::mojom::MediaSessionAction)); MOCK_METHOD1(AddObserver, void(content::MediaSessionObserver*)); + MOCK_METHOD1(AddObserver, + void(media_session::mojom::MediaSessionObserverPtr)); MOCK_METHOD1(RemoveObserver, void(content::MediaSessionObserver*)); + MOCK_METHOD1(GetMediaSessionInfo, void(GetMediaSessionInfoCallback)); + MOCK_METHOD1(GetDebugInfo, void(GetDebugInfoCallback)); private: DISALLOW_COPY_AND_ASSIGN(MockMediaSession);
diff --git a/chromecast/media/DEPS b/chromecast/media/DEPS index 20744a9..88b5631 100644 --- a/chromecast/media/DEPS +++ b/chromecast/media/DEPS
@@ -9,6 +9,7 @@ "+media/audio", "+media/base", "+media/cdm", + "+media/filters", "+mojo/core/embedder/embedder.h", "+mojo/public/cpp/bindings/binding.h", "+ui/gfx/geometry",
diff --git a/chromecast/media/service/cast_mojo_media_client.cc b/chromecast/media/service/cast_mojo_media_client.cc index 90bdd55..04cc924 100644 --- a/chromecast/media/service/cast_mojo_media_client.cc +++ b/chromecast/media/service/cast_mojo_media_client.cc
@@ -4,13 +4,19 @@ #include "chromecast/media/service/cast_mojo_media_client.h" +#include "build/build_config.h" #include "chromecast/media/cma/backend/cma_backend_factory.h" #include "chromecast/media/service/cast_renderer.h" #include "chromecast/public/media/media_pipeline_backend.h" +#include "media/base/audio_decoder.h" #include "media/base/cdm_factory.h" #include "media/base/media_log.h" #include "media/base/overlay_info.h" +#if defined(OS_ANDROID) +#include "media/filters/android/media_codec_audio_decoder.h" +#endif // defined(OS_ANDROID) + namespace chromecast { namespace media { @@ -53,5 +59,13 @@ return create_cdm_factory_cb_.Run(); } +std::unique_ptr<::media::AudioDecoder> CastMojoMediaClient::CreateAudioDecoder( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { +#if defined(OS_ANDROID) + return std::make_unique<::media::MediaCodecAudioDecoder>(task_runner); +#endif // defined(OS_ANDROID) + return nullptr; +} + } // namespace media } // namespace chromecast
diff --git a/chromecast/media/service/cast_mojo_media_client.h b/chromecast/media/service/cast_mojo_media_client.h index 8b9065e0..dbc41d8 100644 --- a/chromecast/media/service/cast_mojo_media_client.h +++ b/chromecast/media/service/cast_mojo_media_client.h
@@ -39,6 +39,8 @@ const std::string& audio_device_id) override; std::unique_ptr<::media::CdmFactory> CreateCdmFactory( service_manager::mojom::InterfaceProvider* host_interfaces) override; + std::unique_ptr<::media::AudioDecoder> CreateAudioDecoder( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) override; private: service_manager::Connector* connector_;
diff --git a/chromeos/services/media_perception/public/mojom/BUILD.gn b/chromeos/services/media_perception/public/mojom/BUILD.gn index 23b62e6..a51e3a7 100644 --- a/chromeos/services/media_perception/public/mojom/BUILD.gn +++ b/chromeos/services/media_perception/public/mojom/BUILD.gn
@@ -6,7 +6,8 @@ mojom("mojom") { sources = [ - "connector.mojom", + "media_perception.mojom", + "media_perception_service.mojom", ] public_deps = [
diff --git a/chromeos/services/media_perception/public/mojom/connector.mojom b/chromeos/services/media_perception/public/mojom/connector.mojom deleted file mode 100644 index 929709c0..0000000 --- a/chromeos/services/media_perception/public/mojom/connector.mojom +++ /dev/null
@@ -1,14 +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. - -module chromeos.media_perception.mojom; - -import "services/video_capture/public/mojom/device_factory.mojom"; - -interface Connector { - // Interface for a process running outside of Chrome to connect to the - // video capture service directly via a Mojo pipe set up through a - // DeviceFactory request. - ConnectToVideoCaptureService(video_capture.mojom.DeviceFactory& request); -};
diff --git a/chromeos/services/media_perception/public/mojom/media_perception.mojom b/chromeos/services/media_perception/public/mojom/media_perception.mojom new file mode 100644 index 0000000..8747054 --- /dev/null +++ b/chromeos/services/media_perception/public/mojom/media_perception.mojom
@@ -0,0 +1,8 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module chromeos.media_perception.mojom; + +interface MediaPerception { +};
diff --git a/chromeos/services/media_perception/public/mojom/media_perception_service.mojom b/chromeos/services/media_perception/public/mojom/media_perception_service.mojom new file mode 100644 index 0000000..12151a5 --- /dev/null +++ b/chromeos/services/media_perception/public/mojom/media_perception_service.mojom
@@ -0,0 +1,29 @@ +// 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. +// +// Next MinVersion: 1 + +module chromeos.media_perception.mojom; + +import "chromeos/services/media_perception/public/mojom/media_perception.mojom"; +import "services/video_capture/public/mojom/device_factory.mojom"; + +// Used to establish two-way communication between a client and the media +// perception service. +interface MediaPerceptionService { + GetController@0(MediaPerceptionController& request, + MediaPerceptionControllerClient client); +}; + +interface MediaPerceptionController { + // Used by the client to establish a MediaPerception pipe. + ActivateMediaPerception@0(MediaPerception& request); +}; + +interface MediaPerceptionControllerClient { + // Interface for the service to connect to the Video Capture Service + // directly via a Mojo pipe set up through a DeviceFactory request. + ConnectToVideoCaptureService@0(video_capture.mojom.DeviceFactory& request); +}; +
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm index 13b03d91..fcf1a395 100644 --- a/components/autofill/ios/browser/autofill_agent.mm +++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -711,11 +711,6 @@ hasUserGesture:(BOOL)hasUserGesture formInMainFrame:(BOOL)formInMainFrame inFrame:(web::WebFrame*)frame { - if (!formInMainFrame) { - // Saving from iframes is not implemented. - return; - } - if (![self isAutofillEnabled]) return;
diff --git a/components/cronet/BUILD.gn b/components/cronet/BUILD.gn index 2804fc69..87a8025 100644 --- a/components/cronet/BUILD.gn +++ b/components/cronet/BUILD.gn
@@ -3,13 +3,17 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/toolchain/toolchain.gni") +import("//build/util/lastchange.gni") import("//build/util/process_version.gni") import("//build/util/version.gni") +import("//components/cronet/native/include/headers.gni") +import("//components/grpc_support/include/headers.gni") import("//testing/test.gni") declare_args() { # If set to true, this will remove histogram manager to reduce binary size. - disable_histogram_support = false + disable_histogram_support = is_mac || is_win } # Disable histogram support is not allowed on Android. @@ -170,4 +174,72 @@ "run_all_unittests.cc", ] } + + _package_dir = "$root_out_dir/cronet" + + # Generate LICENSE file by recursively joining all dependent licenses. + action("generate_license") { + _license_path = "$_package_dir/LICENSE" + + script = "//tools/licenses.py" + inputs = [ + lastchange_file, + ] + outputs = [ + _license_path, + ] + args = [ + "license_file", + rebase_path(_license_path, root_build_dir), + "--gn-target", + "//components/cronet:cronet", + "--gn-out-dir", + ".", + ] + } + + # Copy boiler-plate files into the package. + copy("cronet_package_copy") { + sources = [ + "//AUTHORS", + "//chrome/VERSION", + ] + + outputs = [ + "$_package_dir/{{source_file_part}}", + ] + } + + # Copy shared library adding the version to the file name. + copy("cronet_package_shlib") { + sources = [ + "$root_out_dir/${shlib_prefix}cronet${shlib_extension}", + ] + + outputs = [ + "$_package_dir/${shlib_prefix}cronet.${chrome_version_full}${shlib_extension}", + ] + + deps = [ + ":cronet", + ] + } + + # Copy headers. + copy("cronet_package_headers") { + sources = cronet_native_public_headers + grpc_public_headers + + outputs = [ + "$_package_dir/include/{{source_file_part}}", + ] + } + + group("cronet_package") { + deps = [ + ":cronet_package_copy", + ":cronet_package_headers", + ":cronet_package_shlib", + ":generate_license", + ] + } }
diff --git a/components/cronet/cronet_url_request_context.cc b/components/cronet/cronet_url_request_context.cc index adce07a..a3aa286 100644 --- a/components/cronet/cronet_url_request_context.cc +++ b/components/cronet/cronet_url_request_context.cc
@@ -34,7 +34,6 @@ #include "build/build_config.h" #include "components/cronet/cronet_global_state.h" #include "components/cronet/cronet_prefs_manager.h" -#include "components/cronet/histogram_manager.h" #include "components/cronet/host_cache_persistence_manager.h" #include "components/cronet/url_request_context_config.h" #include "net/base/ip_address.h"
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc index b898de0e..7e5dc6c 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -71,17 +71,6 @@ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); #endif -// Values of the UMA DataReductionProxy.Protocol.NotAcceptingTransform histogram -// defined in metrics/histograms/histograms.xml. This enum must remain -// synchronized with DataReductionProxyProtocolNotAcceptingTransformReason in -// tools/metrics/histograms/enums.xml. -enum NotAcceptingTransformReason { - NOT_ACCEPTING_TRANSFORM_DISABLED = 0, - NOT_ACCEPTING_TRANSFORM_BLACKLISTED = 1, - NOT_ACCEPTING_TRANSFORM_CELLULAR_ONLY = 2, - NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY -}; - // Values of the UMA DataReductionProxy.NetworkChangeEvents histograms. // This enum must remain synchronized with the enum of the same // name in metrics/histograms/histograms.xml. @@ -209,7 +198,6 @@ configurator_(configurator), event_creator_(event_creator), connection_type_(network::mojom::ConnectionType::CONNECTION_UNKNOWN), - ignore_long_term_black_list_rules_(false), network_properties_manager_(nullptr), weak_factory_(this) { DCHECK(io_task_runner_); @@ -792,46 +780,6 @@ return enabled_by_user_ && !unreachable_; } -bool DataReductionProxyConfig::IsBlackListedOrDisabled( - const net::URLRequest& request, - const previews::PreviewsDecider& previews_decider, - previews::PreviewsType previews_type) const { - // Make sure request is not locally blacklisted. - // Pass in net::EFFECTIVE_CONNECTION_TYPE_4G as the threshold since we - // just want to check blacklisting here. - // TODO(crbug.com/720102): Consider new method to just check blacklist. - return !previews_decider.ShouldAllowPreviewAtECT( - request, previews_type, net::EFFECTIVE_CONNECTION_TYPE_4G, - std::vector<std::string>(), ignore_long_term_black_list_rules_); -} - -bool DataReductionProxyConfig::ShouldAcceptServerPreview( - const net::URLRequest& request, - const previews::PreviewsDecider& previews_decider) const { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK((request.load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) != 0); - DCHECK(request.url().SchemeIsHTTPOrHTTPS()); - - if (!previews::params::ArePreviewsAllowed() || - !base::FeatureList::IsEnabled( - features::kDataReductionProxyDecidesTransform)) { - return false; - } - - if (IsBlackListedOrDisabled(request, previews_decider, - previews::PreviewsType::LITE_PAGE) || - IsBlackListedOrDisabled(request, previews_decider, - previews::PreviewsType::LOFI)) { - UMA_HISTOGRAM_ENUMERATION( - "DataReductionProxy.Protocol.NotAcceptingTransform", - NOT_ACCEPTING_TRANSFORM_BLACKLISTED, - NOT_ACCEPTING_TRANSFORM_REASON_BOUNDARY); - return false; - } - - return true; -} - base::TimeTicks DataReductionProxyConfig::GetTicksNow() const { DCHECK(thread_checker_.CalledOnValidThread()); return base::TimeTicks::Now(); @@ -888,15 +836,4 @@ } #endif // defined(OS_CHROMEOS) -void DataReductionProxyConfig::SetIgnoreLongTermBlackListRules( - bool ignore_long_term_black_list_rules) { - DCHECK(thread_checker_.CalledOnValidThread()); - ignore_long_term_black_list_rules_ = ignore_long_term_black_list_rules; -} - -bool DataReductionProxyConfig::IgnoreBlackListLongTermRulesForTesting() const { - DCHECK(thread_checker_.CalledOnValidThread()); - return ignore_long_term_black_list_rules_; -} - } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h index 4d68166..33665e8 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h
@@ -43,10 +43,6 @@ class URLRequestStatus; } // namespace net -namespace previews { -class PreviewsDecider; -} - namespace data_reduction_proxy { class DataReductionProxyConfigValues; @@ -159,15 +155,6 @@ virtual bool ContainsDataReductionProxy( const net::ProxyConfig::ProxyRules& proxy_rules) const; - // Returns whether the client should report to the data reduction proxy that - // it is willing to accept server previews for |request|. - // |previews_decider| is used to check if |request| is locally blacklisted. - // Should only be used if the kDataReductionProxyDecidesTransform feature is - // enabled. - bool ShouldAcceptServerPreview( - const net::URLRequest& request, - const previews::PreviewsDecider& previews_decider) const; - // Returns true if the data saver has been enabled by the user, and the data // saver proxy is reachable. bool enabled_by_user_and_reachable() const; @@ -202,19 +189,12 @@ // TODO(https://crbug.com/821607): Remove after the bug is resolved. void EnableGetNetworkIdAsynchronously(); #endif // defined(OS_CHROMEOS) - - // When triggering previews, prevent long term black list rules. - void SetIgnoreLongTermBlackListRules(bool ignore_long_term_black_list_rules); - // Called when there is a change in the HTTP RTT estimate. void OnRTTOrThroughputEstimatesComputed(base::TimeDelta http_rtt); // Returns the current HTTP RTT estimate. base::Optional<base::TimeDelta> GetHttpRttEstimate() const; - // Returns the value set in SetIgnoreLongTermBlackListRules. - bool IgnoreBlackListLongTermRulesForTesting() const; - protected: virtual base::TimeTicks GetTicksNow() const; @@ -312,11 +292,6 @@ bool is_https, base::TimeDelta* min_retry_delay) const; - // Returns whether the request is blacklisted (or if Lo-Fi is disabled). - bool IsBlackListedOrDisabled( - const net::URLRequest& request, - const previews::PreviewsDecider& previews_decider, - previews::PreviewsType previews_type) const; // Checks if the current network has captive portal, and handles the result. // If the captive portal probe was blocked on the current network, disables @@ -378,9 +353,6 @@ bool warmup_url_fetch_in_flight_secure_proxy_; bool warmup_url_fetch_in_flight_core_proxy_; - // When triggerring previews, prevent long term black list rules. - bool ignore_long_term_black_list_rules_; - // Should be accessed only on the IO thread. Guaranteed to be non-null during // the lifetime of |this| if accessed on the IO thread. NetworkPropertiesManager* network_properties_manager_;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc index 3c5c6458..0c401ed 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -606,7 +606,7 @@ if (!config.has_proxy_config()) return false; - config_->SetIgnoreLongTermBlackListRules( + io_data_->SetIgnoreLongTermBlackListRules( config.ignore_long_term_black_list_rules()); // An empty proxy config is OK, and allows the server to effectively turn off
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc index 90ad9110..e70debbe 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client_unittest.cc
@@ -348,6 +348,10 @@ return test_context_->io_data()->pingback_reporting_fraction(); } + bool ignore_blacklist() const { + return test_context_->io_data()->ignore_blacklist(); + } + void RunUntilIdle() { test_context_->RunUntilIdle(); } @@ -1358,7 +1362,7 @@ config_client()->ApplySerializedConfig( half_reporting_fraction_encoded_config()); EXPECT_EQ(0.5f, pingback_reporting_fraction()); - EXPECT_FALSE(config()->IgnoreBlackListLongTermRulesForTesting()); + EXPECT_FALSE(ignore_blacklist()); } TEST_F(DataReductionProxyConfigServiceClientTest, @@ -1366,7 +1370,7 @@ Init(true); config_client()->ApplySerializedConfig(ignore_black_list_encoded_config()); - EXPECT_TRUE(config()->IgnoreBlackListLongTermRulesForTesting()); + EXPECT_TRUE(ignore_blacklist()); } TEST_F(DataReductionProxyConfigServiceClientTest, EmptyConfigDisablesDRP) {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc index e35d1b5..0b08eb0 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -874,84 +874,6 @@ } } -TEST_F(DataReductionProxyConfigTest, - ShouldAcceptServerPreviewAllPreviewsDisabled) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitFromCommandLine( - "DataReductionProxyDecidesTransform" /* enable_features */, - "Previews" /* disable_features */); - - net::TestURLRequestContext context; - net::TestDelegate delegate; - std::unique_ptr<net::URLRequest> request = - context.CreateRequest(GURL("http://origin.net:80" - ""), - net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); - request->SetLoadFlags(request->load_flags() | - net::LOAD_MAIN_FRAME_DEPRECATED); - std::unique_ptr<previews::TestPreviewsDecider> previews_decider = - std::make_unique<previews::TestPreviewsDecider>(true); - EXPECT_FALSE( - test_config()->ShouldAcceptServerPreview(*request, *previews_decider)); -} - -TEST_F(DataReductionProxyConfigTest, - ShouldAcceptServerPreviewServerPreviewsDisabled) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitFromCommandLine( - "Previews" /* enable_features */, - "DataReductionProxyDecidesTransform" /* disable_features */); - - net::TestURLRequestContext context; - net::TestDelegate delegate; - std::unique_ptr<net::URLRequest> request = - context.CreateRequest(GURL("http://origin.net:80" - ""), - net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); - request->SetLoadFlags(request->load_flags() | - net::LOAD_MAIN_FRAME_DEPRECATED); - std::unique_ptr<previews::TestPreviewsDecider> previews_decider = - std::make_unique<previews::TestPreviewsDecider>(true); - EXPECT_FALSE( - test_config()->ShouldAcceptServerPreview(*request, *previews_decider)); -} - -TEST_F(DataReductionProxyConfigTest, ShouldAcceptServerPreview) { - // Turn on proxy-decides-transform feature to satisfy DCHECK. - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitFromCommandLine( - "Previews,DataReductionProxyDecidesTransform" /* enable_features */, - std::string() /* disable_features */); - base::FieldTrialList field_trial_list(nullptr); - base::FieldTrialList::CreateFieldTrial( - "DataReductionProxyPreviewsBlackListTransition", "Enabled"); - - base::HistogramTester histogram_tester; - net::TestURLRequestContext context_; - net::TestDelegate delegate_; - std::unique_ptr<net::URLRequest> request = - context_.CreateRequest(GURL("http://origin.net:80"), net::IDLE, - &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); - request->SetLoadFlags(request->load_flags() | - net::LOAD_MAIN_FRAME_DEPRECATED); - std::unique_ptr<previews::TestPreviewsDecider> previews_decider = - std::make_unique<previews::TestPreviewsDecider>(true); - - // Verify true for no flags. - EXPECT_TRUE( - test_config()->ShouldAcceptServerPreview(*request, *previews_decider)); - - // Verify PreviewsDecider check. - base::CommandLine::ForCurrentProcess()->InitFromArgv(0, nullptr); - previews_decider = std::make_unique<previews::TestPreviewsDecider>(false); - EXPECT_FALSE( - test_config()->ShouldAcceptServerPreview(*request, *previews_decider)); - histogram_tester.ExpectBucketCount( - "DataReductionProxy.Protocol.NotAcceptingTransform", - 1 /* NOT_ACCEPTING_TRANSFORM_BLACKLISTED */, 1); - previews_decider = std::make_unique<previews::TestPreviewsDecider>(true); -} - TEST_F(DataReductionProxyConfigTest, HandleWarmupFetcherResponse) { base::HistogramTester histogram_tester; const net::URLRequestStatus kSuccess(net::URLRequestStatus::SUCCESS, net::OK);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc index 8ee53dc8..d0987c1 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -30,7 +30,6 @@ #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" -#include "components/previews/core/previews_decider.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" #include "net/url_request/http_user_agent_settings.h" @@ -340,15 +339,13 @@ config_client_->ApplySerializedConfig(serialized_config); } -bool DataReductionProxyIOData::ShouldAcceptServerPreview( - const net::URLRequest& request, - previews::PreviewsDecider* previews_decider) { - DCHECK(previews_decider); - DCHECK((request.load_flags() & net::LOAD_MAIN_FRAME_DEPRECATED) != 0); - if (!config_ || !request.url().SchemeIsHTTPOrHTTPS()) { - return false; - } - return config_->ShouldAcceptServerPreview(request, *previews_decider); +void DataReductionProxyIOData::SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) { + ui_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &DataReductionProxyService::SetIgnoreLongTermBlackListRules, service_, + ignore_long_term_black_list_rules)); } void DataReductionProxyIOData::UpdateDataUseForHost(int64_t network_bytes,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h index 171a49b4..79b7c7dc 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -41,10 +41,6 @@ class NetworkConnectionTracker; } -namespace previews { -class PreviewsDecider; -} - namespace data_reduction_proxy { class DataReductionProxyBypassStats; @@ -107,12 +103,10 @@ // Applies a serialized Data Reduction Proxy configuration. void SetDataReductionProxyConfiguration(const std::string& serialized_config); - // Returns true when server previews should be activated. When server previews - // are active, URL requests are modified to request low fidelity versions of - // the resources.|previews_decider| is a non-null object that determines - // eligibility of showing the preview based on past opt outs. - bool ShouldAcceptServerPreview(const net::URLRequest& request, - previews::PreviewsDecider* previews_decider); + // When triggering previews, prevent long term black list rules. Overridden in + // testing. + virtual void SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules); // Bridge methods to safely call to the UI thread objects. void UpdateDataUseForHost(int64_t network_bytes,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc index 2fb9cb23..2c1c7cdc 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -913,11 +913,6 @@ TEST_F(DataReductionProxyNetworkDelegateTest, LoFiTransitions) { Init(USE_INSECURE_PROXY, false); - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeatures( - {previews::features::kPreviews, - features::kDataReductionProxyDecidesTransform}, - {}); // Enable Lo-Fi. bool is_data_reduction_proxy_enabled[] = {false, true}; @@ -945,16 +940,13 @@ std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); - lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview( - *fake_request, test_previews_decider)); + lofi_decider()->SetIsUsingLoFi(true); NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); VerifyHeaders(is_data_reduction_proxy_enabled[i], true, headers); VerifyDataReductionProxyData(*fake_request, - is_data_reduction_proxy_enabled[i], - config()->ShouldAcceptServerPreview( - *fake_request, test_previews_decider)); + is_data_reduction_proxy_enabled[i], true); } { @@ -1028,14 +1020,11 @@ std::unique_ptr<net::URLRequest> fake_request = context()->CreateRequest( GURL(kTestURL), net::IDLE, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); fake_request->SetLoadFlags(net::LOAD_MAIN_FRAME_DEPRECATED); - lofi_decider()->SetIsUsingLoFi(config()->ShouldAcceptServerPreview( - *fake_request, test_previews_decider)); + lofi_decider()->SetIsUsingLoFi(true); NotifyNetworkDelegate(fake_request.get(), data_reduction_proxy_info, proxy_retry_info, &headers); VerifyDataReductionProxyData(*fake_request, - is_data_reduction_proxy_enabled[i], - config()->ShouldAcceptServerPreview( - *fake_request, test_previews_decider)); + is_data_reduction_proxy_enabled[i], true); } } }
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc index f4fa66ea..3f2e4d5 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -275,6 +275,12 @@ settings_->SetProxyRequestHeaders(headers); } +void DataReductionProxyService::SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + settings_->SetIgnoreLongTermBlackListRules(ignore_long_term_black_list_rules); +} + void DataReductionProxyService::LoadHistoricalDataUsage( const HistoricalDataUsageCallback& load_data_usage_callback) { std::unique_ptr<std::vector<DataUsageBucket>> data_usage(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h index 5f7e563..320e0097 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -143,6 +143,9 @@ // cleared. void OnCacheCleared(const base::Time start, const base::Time end); + // When triggering previews, prevent long term black list rules. + void SetIgnoreLongTermBlackListRules(bool ignore_long_term_black_list_rules); + // Returns the current network quality estimates. net::EffectiveConnectionType GetEffectiveConnectionType() const; base::Optional<base::TimeDelta> GetHttpRttEstimate() const;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h index feafcc4..a0289618 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h
@@ -143,6 +143,10 @@ // some of them should have. bool IsDataReductionProxyUnreachable(); + // When triggering previews, prevent long term black list rules. + virtual void SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) {} + ContentLengthList GetDailyContentLengths(const char* pref_name); // Configures data reduction proxy. |at_startup| is true when this method is
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc index 79f146d8..f206866 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -294,6 +294,11 @@ pingback_reporting_fraction_ = pingback_reporting_fraction; } +void TestDataReductionProxyIOData::SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) { + ignore_blacklist_ = ignore_long_term_black_list_rules; +} + void TestDataReductionProxyIOData::SetDataReductionProxyService( base::WeakPtr<DataReductionProxyService> data_reduction_proxy_service) { if (!service_set_)
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h index 4537187..157997d 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -263,10 +263,16 @@ // Records the reporting fraction that was set by parsing a config. void SetPingbackReportingFraction(float pingback_reporting_fraction) override; + // Records |ignore_long_term_black_list_rules| as |ignore_blacklist_|. + void SetIgnoreLongTermBlackListRules( + bool ignore_long_term_black_list_rules) override; + float pingback_reporting_fraction() const { return pingback_reporting_fraction_; } + bool ignore_blacklist() const { return ignore_blacklist_; } + private: // Allowed SetDataReductionProxyService to be re-entrant. bool service_set_; @@ -274,6 +280,9 @@ // Reporting fraction last set via SetPingbackReportingFraction. float pingback_reporting_fraction_; + // Whether the long term blacklist rules should be ignored. + bool ignore_blacklist_ = false; + TestDataReductionProxyRequestOptions* test_request_options_; };
diff --git a/components/feature_engagement/public/event_constants.cc b/components/feature_engagement/public/event_constants.cc index 3700e12..59e6061 100644 --- a/components/feature_engagement/public/event_constants.cc +++ b/components/feature_engagement/public/event_constants.cc
@@ -22,6 +22,7 @@ const char kIncognitoWindowSessionTimeMet[] = "incognito_window_session_time_met"; +const char kReopenTabConditionsMet[] = "reopen_tab_conditions_met"; #endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/feature_engagement/public/event_constants.h b/components/feature_engagement/public/event_constants.h index d45d76a..45c30820 100644 --- a/components/feature_engagement/public/event_constants.h +++ b/components/feature_engagement/public/event_constants.h
@@ -40,6 +40,11 @@ // IncognitoWindowPromo by accumulating 2 hours of active session time (one-off // event). extern const char kIncognitoWindowSessionTimeMet[]; + +// All conditions for reopen closed tab IPH were met. Since this IPH needs to +// track user events (opening/closing tabs, focusing the omnibox, etc) on the +// second level, it must be done manually. +extern const char kReopenTabConditionsMet[]; #endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_IOS)
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc index 5f7aad7e..5c33fea 100644 --- a/components/feature_engagement/public/feature_constants.cc +++ b/components/feature_engagement/public/feature_constants.cc
@@ -66,6 +66,8 @@ "IPH_IncognitoWindow", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kIPHNewTabFeature{"IPH_NewTab", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kIPHReopenTabFeature{"IPH_ReopenTab", + base::FEATURE_DISABLED_BY_DEFAULT}; #endif // BUILDFLAG(ENABLE_DESKTOP_IPH) #if defined(OS_IOS)
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h index 93af7eb..bce43b7 100644 --- a/components/feature_engagement/public/feature_constants.h +++ b/components/feature_engagement/public/feature_constants.h
@@ -48,6 +48,7 @@ extern const base::Feature kIPHBookmarkFeature; extern const base::Feature kIPHIncognitoWindowFeature; extern const base::Feature kIPHNewTabFeature; +extern const base::Feature kIPHReopenTabFeature; #endif // BUILDFLAG(ENABLE_DESKTOP_IPH) #if defined(OS_IOS)
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc index 6dd4be7..932a6d9 100644 --- a/components/feature_engagement/public/feature_list.cc +++ b/components/feature_engagement/public/feature_list.cc
@@ -42,6 +42,7 @@ &kIPHBookmarkFeature, &kIPHIncognitoWindowFeature, &kIPHNewTabFeature, + &kIPHReopenTabFeature, #endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) #if defined(OS_IOS) &kIPHBottomToolbarTipFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h index 6f834901..334238c5 100644 --- a/components/feature_engagement/public/feature_list.h +++ b/components/feature_engagement/public/feature_list.h
@@ -81,6 +81,7 @@ DEFINE_VARIATION_PARAM(kIPHBookmarkFeature, "IPH_Bookmark"); DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow"); DEFINE_VARIATION_PARAM(kIPHNewTabFeature, "IPH_NewTab"); +DEFINE_VARIATION_PARAM(kIPHReopenTabFeature, "IPH_ReopenTab"); #endif // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) #if defined(OS_IOS) DEFINE_VARIATION_PARAM(kIPHBottomToolbarTipFeature, "IPH_BottomToolbarTip"); @@ -124,6 +125,7 @@ VARIATION_ENTRY(kIPHBookmarkFeature), VARIATION_ENTRY(kIPHIncognitoWindowFeature), VARIATION_ENTRY(kIPHNewTabFeature), + VARIATION_ENTRY(kIPHReopenTabFeature), #elif defined(OS_IOS) VARIATION_ENTRY(kIPHBottomToolbarTipFeature), VARIATION_ENTRY(kIPHLongPressToolbarTipFeature),
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc index a62125b..996ccac 100644 --- a/components/previews/content/previews_decider_impl.cc +++ b/components/previews/content/previews_decider_impl.cc
@@ -112,6 +112,7 @@ std::unique_ptr<PreviewsOptimizationGuide> previews_opt_guide, const PreviewsIsEnabledCallback& is_enabled_callback, blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(ui_task_runner_->BelongsToCurrentThread()); is_enabled_callback_ = is_enabled_callback; previews_ui_service_ = previews_ui_service; @@ -127,6 +128,7 @@ void PreviewsDeciderImpl::OnNewBlacklistedHost(const std::string& host, base::Time time) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); ui_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PreviewsUIService::OnNewBlacklistedHost, @@ -134,6 +136,7 @@ } void PreviewsDeciderImpl::OnUserBlacklistedStatusChange(bool blacklisted) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); ui_task_runner_->PostTask( FROM_HERE, @@ -142,6 +145,7 @@ } void PreviewsDeciderImpl::OnBlacklistCleared(base::Time time) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); ui_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PreviewsUIService::OnBlacklistCleared, @@ -151,6 +155,7 @@ void PreviewsDeciderImpl::InitializeOnIOThread( std::unique_ptr<blacklist::OptOutStore> previews_opt_out_store, blacklist::BlacklistData::AllowedTypesAndVersions allowed_previews) { + DETACH_FROM_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); previews_black_list_.reset( new PreviewsBlackList(std::move(previews_opt_out_store), clock_, this, @@ -164,6 +169,7 @@ void PreviewsDeciderImpl::OnResourceLoadingHints( const GURL& document_gurl, const std::vector<std::string>& patterns_to_block) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); ui_task_runner_->PostTask( FROM_HERE, @@ -182,6 +188,7 @@ PreviewsType type, base::Time time, uint64_t page_id) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ui_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PreviewsUIService::LogPreviewNavigation, @@ -195,6 +202,7 @@ PreviewsType type, std::vector<PreviewsEligibilityReason>&& passed_reasons, uint64_t page_id) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); LogPreviewsEligibilityReason(reason, type); ui_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PreviewsUIService::LogPreviewDecisionMade, @@ -206,6 +214,7 @@ bool opt_out, PreviewsType type, uint64_t page_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); base::Time time = previews_black_list_->AddPreviewNavigation(url, opt_out, type); @@ -217,11 +226,13 @@ void PreviewsDeciderImpl::ClearBlackList(base::Time begin_time, base::Time end_time) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); previews_black_list_->ClearBlackList(begin_time, end_time); } void PreviewsDeciderImpl::SetIgnorePreviewsBlacklistDecision(bool ignored) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(io_task_runner_->BelongsToCurrentThread()); blacklist_ignored_ = ignored; ui_task_runner_->PostTask( @@ -232,6 +243,7 @@ bool PreviewsDeciderImpl::ShouldAllowPreview(const net::URLRequest& request, PreviewsType type) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(type == PreviewsType::OFFLINE || type == PreviewsType::LITE_PAGE_REDIRECT || type == PreviewsType::NOSCRIPT || @@ -248,7 +260,8 @@ PreviewsType type, net::EffectiveConnectionType effective_connection_type_threshold, const std::vector<std::string>& host_blacklist_from_finch, - bool ignore_long_term_black_list_rules) const { + bool is_server_preview) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!previews::params::ArePreviewsAllowed()) { return false; } @@ -289,7 +302,8 @@ // The blacklist will disallow certain hosts for periods of time based on // user's opting out of the preview. PreviewsEligibilityReason status = previews_black_list_->IsLoadedAndAllowed( - request.url(), type, ignore_long_term_black_list_rules, + request.url(), type, + is_server_preview && ignore_long_term_blacklist_for_server_previews_, &passed_reasons); if (status != PreviewsEligibilityReason::ALLOWED) { @@ -408,6 +422,7 @@ bool PreviewsDeciderImpl::IsURLAllowedForPreview(const net::URLRequest& request, PreviewsType type) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(PreviewsType::NOSCRIPT == type || PreviewsType::RESOURCE_LOADING_HINTS == type); if (previews_black_list_ && !blacklist_ignored_) { @@ -451,6 +466,7 @@ const net::URLRequest& request, PreviewsType type, std::vector<PreviewsEligibilityReason>* passed_reasons) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(type == PreviewsType::LITE_PAGE_REDIRECT || type == PreviewsType::NOSCRIPT || type == PreviewsType::RESOURCE_LOADING_HINTS); @@ -493,6 +509,7 @@ const net::URLRequest& request, PreviewsType type, std::vector<PreviewsEligibilityReason>* passed_reasons) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(type == PreviewsType::LITE_PAGE_REDIRECT || type == PreviewsType::NOSCRIPT || type == PreviewsType::RESOURCE_LOADING_HINTS); @@ -516,4 +533,11 @@ return ++page_id_; } +void PreviewsDeciderImpl::SetIgnoreLongTermBlackListForServerPreviews( + bool ignore_long_term_blacklist_for_server_previews) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + ignore_long_term_blacklist_for_server_previews_ = + ignore_long_term_blacklist_for_server_previews; +} + } // namespace previews
diff --git a/components/previews/content/previews_decider_impl.h b/components/previews/content/previews_decider_impl.h index 4691a42..df2e37b 100644 --- a/components/previews/content/previews_decider_impl.h +++ b/components/previews/content/previews_decider_impl.h
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" #include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h" @@ -117,10 +118,16 @@ PreviewsType type, net::EffectiveConnectionType effective_connection_type_threshold, const std::vector<std::string>& host_blacklist_from_finch, - bool ignore_long_term_black_list_rules) const override; + bool is_server_preview) const override; bool IsURLAllowedForPreview(const net::URLRequest& request, PreviewsType type) const override; + // Set whether ignoring the long term blacklist rules is allowed for calls to + // ShouldAllowPreviewAtECT that have |can_ignore_long_term_black_list_rules| + // set to true. + void SetIgnoreLongTermBlackListForServerPreviews( + bool ignore_long_term_blacklist_for_server_previews); + void LoadResourceHints(const net::URLRequest& request) override; // Generates a page ID that is guaranteed to be unique from any other page ID @@ -176,8 +183,15 @@ // Whether the decisions made by PreviewsBlackList should be ignored or not. // This can be changed by chrome://interventions-internals to test/debug the // behavior of Previews decisions. + // This is related to a test flag and should only be true when the user has + // set it in flags. See previews::IsPreviewsBlacklistIgnoredViaFlag. bool blacklist_ignored_; + // Whether ignoring the blacklist is allowed for calls to + // ShouldAllowPreviewAtECT that have + // |is_server_preview| true. + bool ignore_long_term_blacklist_for_server_previews_ = false; + base::Clock* clock_; // The UI and IO thread task runners. |ui_task_runner_| is used to post @@ -192,6 +206,8 @@ uint64_t page_id_; + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<PreviewsDeciderImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(PreviewsDeciderImpl);
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc index fd5755e..73a16c873 100644 --- a/components/previews/content/previews_decider_impl_unittest.cc +++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -9,6 +9,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "base/bind.h" #include "base/bind_helpers.h" @@ -105,13 +106,14 @@ PreviewsType type, bool ignore_long_term_black_list_rules, std::vector<PreviewsEligibilityReason>* passed_reasons) const override { - PreviewsEligibilityReason ordered_reasons[] = { + std::vector<PreviewsEligibilityReason> ordered_reasons = { PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED, - PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT, - PreviewsEligibilityReason::USER_BLACKLISTED, - PreviewsEligibilityReason::HOST_BLACKLISTED, - PreviewsEligibilityReason::ALLOWED, - }; + PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT}; + + if (!ignore_long_term_black_list_rules) { + ordered_reasons.push_back(PreviewsEligibilityReason::USER_BLACKLISTED); + ordered_reasons.push_back(PreviewsEligibilityReason::HOST_BLACKLISTED); + } for (auto reason : ordered_reasons) { if (status_ == reason) { @@ -119,8 +121,8 @@ } passed_reasons->push_back(reason); } - NOTREACHED(); - return status_; + + return PreviewsEligibilityReason::ALLOWED; } private: @@ -1785,6 +1787,33 @@ EXPECT_EQ(page_id_set.end(), page_id_set.find(0u)); } +TEST_F(PreviewsDeciderImplTest, TestIgnoreLongTermRule) { + // Verify that when long term rules can be ignored, and the caller is fine + // with ignoring long term rules, they are not checked. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kPreviews); + InitializeUIService(); + + previews_decider_impl()->SetIgnoreLongTermBlackListForServerPreviews(true); + + std::unique_ptr<TestPreviewsBlackList> blacklist = + std::make_unique<TestPreviewsBlackList>( + PreviewsEligibilityReason::HOST_BLACKLISTED, previews_decider_impl()); + previews_decider_impl()->InjectTestBlacklist(std::move(blacklist)); + + // LoFi and LitePage check NQE on their own. + network_quality_estimator()->set_effective_connection_type( + net::EFFECTIVE_CONNECTION_TYPE_3G); + + base::HistogramTester histogram_tester; + EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtECT( + *CreateRequest(), PreviewsType::LITE_PAGE, + net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>(), false)); + EXPECT_TRUE(previews_decider_impl()->ShouldAllowPreviewAtECT( + *CreateRequest(), PreviewsType::LITE_PAGE, + net::EFFECTIVE_CONNECTION_TYPE_4G, std::vector<std::string>(), true)); +} + } // namespace } // namespace previews
diff --git a/components/previews/content/previews_ui_service.cc b/components/previews/content/previews_ui_service.cc index 0ff68ee..f661fe0 100644 --- a/components/previews/content/previews_ui_service.cc +++ b/components/previews/content/previews_ui_service.cc
@@ -124,6 +124,17 @@ return logger_.get(); } +// When triggering previews, prevent long term black list rules. +void PreviewsUIService::SetIgnoreLongTermBlackListForServerPreviews( + bool ignore_long_term_black_list_rules_allowed) { + DCHECK(thread_checker_.CalledOnValidThread()); + io_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &PreviewsDeciderImpl::SetIgnoreLongTermBlackListForServerPreviews, + previews_decider_impl_, ignore_long_term_black_list_rules_allowed)); +} + void PreviewsUIService::ClearBlackList(base::Time begin_time, base::Time end_time) { DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/components/previews/content/previews_ui_service.h b/components/previews/content/previews_ui_service.h index 0c4ffdb2..981fe04 100644 --- a/components/previews/content/previews_ui_service.h +++ b/components/previews/content/previews_ui_service.h
@@ -128,6 +128,10 @@ // return null. PreviewsLogger* previews_logger() const; + // When triggering previews, prevent long term black list rules. + void SetIgnoreLongTermBlackListForServerPreviews( + bool ignore_long_term_black_list_rules_allowed); + private: // The IO thread portion of the inter-thread communication for previews/. base::WeakPtr<previews::PreviewsDeciderImpl> previews_decider_impl_;
diff --git a/components/previews/core/previews_decider.h b/components/previews/core/previews_decider.h index b2fa370..41dbb10 100644 --- a/components/previews/core/previews_decider.h +++ b/components/previews/core/previews_decider.h
@@ -26,12 +26,15 @@ // preview will be disallowed; preview types that check network quality before // calling ShouldAllowPreviewAtECT should pass in // EFFECTIVE_CONNECTION_TYPE_4G. + // |is_server_preview| means that the blacklist does + // not need to be checked for long term rules when Previews has been + // configured to allow skipping the blacklist. virtual bool ShouldAllowPreviewAtECT( const net::URLRequest& request, PreviewsType type, net::EffectiveConnectionType effective_connection_type_threshold, const std::vector<std::string>& host_blacklist_from_finch, - bool ignore_long_term_black_list_rules) const = 0; + bool is_server_preview) const = 0; // Same as ShouldAllowPreviewAtECT, but uses the previews default // EffectiveConnectionType and no blacklisted hosts from the server.
diff --git a/components/previews/core/test_previews_decider.cc b/components/previews/core/test_previews_decider.cc index a90d491..247e815 100644 --- a/components/previews/core/test_previews_decider.cc +++ b/components/previews/core/test_previews_decider.cc
@@ -16,7 +16,7 @@ previews::PreviewsType type, net::EffectiveConnectionType effective_connection_type_threshold, const std::vector<std::string>& host_blacklist_from_server, - bool ignore_long_term_black_list_rules) const { + bool is_server_preview) const { return allow_previews_; }
diff --git a/components/previews/core/test_previews_decider.h b/components/previews/core/test_previews_decider.h index 70d00cb0c..1b76db7 100644 --- a/components/previews/core/test_previews_decider.h +++ b/components/previews/core/test_previews_decider.h
@@ -21,7 +21,7 @@ previews::PreviewsType type, net::EffectiveConnectionType effective_connection_type_threshold, const std::vector<std::string>& host_blacklist_from_server, - bool ignore_long_term_black_list_rules) const override; + bool is_server_preview) const override; bool ShouldAllowPreview(const net::URLRequest& request, previews::PreviewsType type) const override; bool IsURLAllowedForPreview(const net::URLRequest& request,
diff --git a/components/signin/ios/DEPS b/components/signin/ios/DEPS index 4dd6307..4acb57d 100644 --- a/components/signin/ios/DEPS +++ b/components/signin/ios/DEPS
@@ -1,4 +1,6 @@ include_rules = [ "+ios/web/public", + "+services/network/public", + "+services/network/test", "+third_party/ocmock", ]
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper.cc b/components/signin/ios/browser/wait_for_network_callback_helper.cc index fcec9a5f..cb7ef81 100644 --- a/components/signin/ios/browser/wait_for_network_callback_helper.cc +++ b/components/signin/ios/browser/wait_for_network_callback_helper.cc
@@ -4,17 +4,19 @@ #include "components/signin/ios/browser/wait_for_network_callback_helper.h" -WaitForNetworkCallbackHelper::WaitForNetworkCallbackHelper() { - net::NetworkChangeNotifier::AddNetworkChangeObserver(this); +WaitForNetworkCallbackHelper::WaitForNetworkCallbackHelper( + network::NetworkConnectionTracker* network_connection_tracker) + : network_connection_tracker_(network_connection_tracker) { + network_connection_tracker_->AddNetworkConnectionObserver(this); } WaitForNetworkCallbackHelper::~WaitForNetworkCallbackHelper() { - net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); + network_connection_tracker_->RemoveNetworkConnectionObserver(this); } -void WaitForNetworkCallbackHelper::OnNetworkChanged( - net::NetworkChangeNotifier::ConnectionType type) { - if (net::NetworkChangeNotifier::IsOffline()) +void WaitForNetworkCallbackHelper::OnConnectionChanged( + network::mojom::ConnectionType type) { + if (network_connection_tracker_->IsOffline()) return; for (const base::Closure& callback : delayed_callbacks_) @@ -25,7 +27,7 @@ void WaitForNetworkCallbackHelper::HandleCallback( const base::Closure& callback) { - if (net::NetworkChangeNotifier::IsOffline()) { + if (network_connection_tracker_->IsOffline()) { delayed_callbacks_.push_back(callback); } else { callback.Run();
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper.h b/components/signin/ios/browser/wait_for_network_callback_helper.h index 6e77513..37c4ede 100644 --- a/components/signin/ios/browser/wait_for_network_callback_helper.h +++ b/components/signin/ios/browser/wait_for_network_callback_helper.h
@@ -9,25 +9,26 @@ #include "base/callback.h" #include "base/macros.h" -#include "net/base/network_change_notifier.h" +#include "services/network/public/cpp/network_connection_tracker.h" // Class used for delaying callbacks when the network connection is offline and // invoking them when the network connection becomes online. class WaitForNetworkCallbackHelper - : public net::NetworkChangeNotifier::NetworkChangeObserver { + : public network::NetworkConnectionTracker::NetworkConnectionObserver { public: - WaitForNetworkCallbackHelper(); + WaitForNetworkCallbackHelper( + network::NetworkConnectionTracker* network_connection_tracker); ~WaitForNetworkCallbackHelper() override; - // net::NetworkChangeController::NetworkChangeObserver implementation. - void OnNetworkChanged( - net::NetworkChangeNotifier::ConnectionType type) override; + // network::NetworkConnectionTracker::NetworkConnectionObserver implementation + void OnConnectionChanged(network::mojom::ConnectionType type) override; // If offline, saves the |callback| to be called later when online. Otherwise, // invokes immediately. void HandleCallback(const base::Closure& callback); private: + network::NetworkConnectionTracker* network_connection_tracker_; std::list<base::Closure> delayed_callbacks_; DISALLOW_COPY_AND_ASSIGN(WaitForNetworkCallbackHelper);
diff --git a/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc b/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc index 78fe1ca..80ae7b2 100644 --- a/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc +++ b/components/signin/ios/browser/wait_for_network_callback_helper_unittest.cc
@@ -7,7 +7,7 @@ #include "base/bind.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" -#include "net/base/mock_network_change_notifier.h" +#include "services/network/test/test_network_connection_tracker.h" #include "testing/gtest/include/gtest/gtest.h" // A test fixture to test WaitForNetworkCallbackHelper. @@ -16,17 +16,19 @@ void CallbackFunction() { num_callbacks_invoked_++; } protected: - WaitForNetworkCallbackHelperTest() : num_callbacks_invoked_(0) {} + WaitForNetworkCallbackHelperTest() + : num_callbacks_invoked_(0), + callback_helper_(network::TestNetworkConnectionTracker::GetInstance()) { + } int num_callbacks_invoked_; base::test::ScopedTaskEnvironment scoped_task_environment_; - net::test::MockNetworkChangeNotifier network_change_notifier_; WaitForNetworkCallbackHelper callback_helper_; }; TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedImmediately) { - network_change_notifier_.SetConnectionType( - net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); + network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType( + network::mojom::ConnectionType::CONNECTION_WIFI); callback_helper_.HandleCallback( base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, base::Unretained(this))); @@ -34,8 +36,8 @@ } TEST_F(WaitForNetworkCallbackHelperTest, CallbackInvokedLater) { - network_change_notifier_.SetConnectionType( - net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE); + network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType( + network::mojom::ConnectionType::CONNECTION_NONE); callback_helper_.HandleCallback( base::Bind(&WaitForNetworkCallbackHelperTest::CallbackFunction, base::Unretained(this))); @@ -44,10 +46,8 @@ base::Unretained(this))); EXPECT_EQ(0, num_callbacks_invoked_); - network_change_notifier_.SetConnectionType( - net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); - network_change_notifier_.NotifyObserversOfConnectionTypeChangeForTests( - net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI); + network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType( + network::mojom::ConnectionType::CONNECTION_WIFI); scoped_task_environment_.RunUntilIdle(); EXPECT_EQ(2, num_callbacks_invoked_); }
diff --git a/components/sync/model_impl/model_type_store_backend.cc b/components/sync/model_impl/model_type_store_backend.cc index 43315bb..9bf4379 100644 --- a/components/sync/model_impl/model_type_store_backend.cc +++ b/components/sync/model_impl/model_type_store_backend.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "components/sync/protocol/model_type_store_schema_descriptor.pb.h" @@ -29,6 +30,11 @@ const char ModelTypeStoreBackend::kStoreInitResultHistogramName[] = "Sync.ModelTypeStoreInitResult"; +const base::Feature kModelTypeStoreAvoidReadCache{ + "kModelTypeStoreAvoidReadCache", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kModelTypeStoreSmallWriteBufferSize{ + "kModelTypeStoreSmallWriteBufferSize", base::FEATURE_DISABLED_BY_DEFAULT}; + namespace { StoreInitResultForHistogram LevelDbStatusToStoreInitResult( @@ -133,6 +139,10 @@ leveldb_env::Options options; options.create_if_missing = true; options.paranoid_checks = true; + + if (base::FeatureList::IsEnabled(kModelTypeStoreSmallWriteBufferSize)) + options.write_buffer_size = 512 * 1024; + if (env) options.env = env; @@ -157,6 +167,8 @@ record_list->reserve(id_list.size()); leveldb::ReadOptions read_options; read_options.verify_checksums = true; + if (base::FeatureList::IsEnabled(kModelTypeStoreAvoidReadCache)) + read_options.fill_cache = false; std::string key; std::string value; for (const std::string& id : id_list) { @@ -180,6 +192,8 @@ DCHECK(db_); leveldb::ReadOptions read_options; read_options.verify_checksums = true; + if (base::FeatureList::IsEnabled(kModelTypeStoreAvoidReadCache)) + read_options.fill_cache = false; std::unique_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options)); const leveldb::Slice prefix_slice(prefix); for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) { @@ -211,8 +225,10 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(db_); leveldb::WriteBatch write_batch; - std::unique_ptr<leveldb::Iterator> iter( - db_->NewIterator(leveldb::ReadOptions())); + leveldb::ReadOptions read_options; + if (base::FeatureList::IsEnabled(kModelTypeStoreAvoidReadCache)) + read_options.fill_cache = false; + std::unique_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options)); const leveldb::Slice prefix_slice(prefix); for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) { leveldb::Slice key = iter->key(); @@ -240,6 +256,8 @@ DCHECK(db_); leveldb::ReadOptions read_options; read_options.verify_checksums = true; + if (base::FeatureList::IsEnabled(kModelTypeStoreAvoidReadCache)) + read_options.fill_cache = false; std::string value; ModelTypeStoreSchemaDescriptor schema_descriptor; leveldb::Status status =
diff --git a/components/variations/client_filterable_state.h b/components/variations/client_filterable_state.h index a244f79..bd2f86d 100644 --- a/components/variations/client_filterable_state.h +++ b/components/variations/client_filterable_state.h
@@ -47,6 +47,11 @@ // Based on base::SysInfo::IsLowEndDevice(). bool is_low_end_device = false; + // Whether this platform supports experiments which retain their group + // assignments across runs. + // TODO(paulmiller): Remove this once https://crbug.com/866722 is resolved. + bool supports_permanent_consistency = true; + // The country code to use for studies configured with session consistency. std::string session_consistency_country;
diff --git a/components/variations/service/BUILD.gn b/components/variations/service/BUILD.gn index e3c0c1e..c77020f 100644 --- a/components/variations/service/BUILD.gn +++ b/components/variations/service/BUILD.gn
@@ -12,6 +12,7 @@ "variations_field_trial_creator.h", "variations_service.cc", "variations_service.h", + "variations_service_client.cc", "variations_service_client.h", ]
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc index 2dadabf..0797532 100644 --- a/components/variations/service/variations_field_trial_creator.cc +++ b/components/variations/service/variations_field_trial_creator.cc
@@ -250,6 +250,8 @@ // evaluated, that field trial would not be able to apply for this case. state->is_low_end_device = base::SysInfo::IsLowEndDevice(); #endif + state->supports_permanent_consistency = + client_->GetSupportsPermanentConsistency(); state->session_consistency_country = GetLatestCountry(); state->permanent_consistency_country = LoadPermanentConsistencyCountry( version, state->session_consistency_country);
diff --git a/components/variations/service/variations_service_client.cc b/components/variations/service/variations_service_client.cc new file mode 100644 index 0000000..c2baa85c --- /dev/null +++ b/components/variations/service/variations_service_client.cc
@@ -0,0 +1,13 @@ +// 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 "components/variations/service/variations_service_client.h" + +namespace variations { + +bool VariationsServiceClient::GetSupportsPermanentConsistency() { + return true; +} + +} // namespace variations
diff --git a/components/variations/service/variations_service_client.h b/components/variations/service/variations_service_client.h index 2b466bb..4e2c644 100644 --- a/components/variations/service/variations_service_client.h +++ b/components/variations/service/variations_service_client.h
@@ -45,6 +45,11 @@ // Gets the channel of the embedder. virtual version_info::Channel GetChannel() = 0; + // Gets whether this platform supports experiments which retain their group + // assignments across runs. + // TODO(paulmiller): Remove this once https://crbug.com/866722 is resolved. + virtual bool GetSupportsPermanentConsistency(); + // Returns whether the embedder overrides the value of the restrict parameter. // |parameter| is an out-param that will contain the value of the restrict // parameter if true is returned.
diff --git a/components/variations/study_filtering.cc b/components/variations/study_filtering.cc index eaca58a..6f1e2177 100644 --- a/components/variations/study_filtering.cc +++ b/components/variations/study_filtering.cc
@@ -12,6 +12,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "components/variations/client_filterable_state.h" +#include "components/variations/proto/study.pb.h" namespace variations { namespace { @@ -266,6 +267,14 @@ } } + // TODO(paulmiller): Remove this once https://crbug.com/866722 is resolved. + if (study.consistency() == Study_Consistency_PERMANENT && + !client_state.supports_permanent_consistency) { + DVLOG(1) << "Filtered out study " << study.name() + << " due to supports_permanent_consistency."; + return false; + } + DVLOG(1) << "Kept study " << study.name() << "."; return true; }
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index cf8c28f..cb7f3f1 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -493,14 +493,9 @@ current_paint_.setFilterQuality(kLow_SkFilterQuality); } - if (quad->ShouldDrawWithBlending() || - quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) { - current_paint_.setAlpha(quad->shared_quad_state->opacity * 255); - current_paint_.setBlendMode( - static_cast<SkBlendMode>(quad->shared_quad_state->blend_mode)); - } else { - current_paint_.setBlendMode(SkBlendMode::kSrc); - } + current_paint_.setAlpha(quad->shared_quad_state->opacity * 255); + current_paint_.setBlendMode( + static_cast<SkBlendMode>(quad->shared_quad_state->blend_mode)); if (draw_region) { gfx::QuadF local_draw_region(*draw_region);
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index 1d6edc8..75fdc1f6 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -392,6 +392,14 @@ // to determine the freshness of a surface at aggregation time. const LocalSurfaceId& last_created_local_surface_id = last_created_surface_id_.local_surface_id(); + bool last_surface_has_dependent_frame = + prev_surface && prev_surface->HasDependentFrame(); + + bool child_initiated_synchronization_event = + last_created_local_surface_id.is_valid() && + local_surface_id.child_sequence_number() > + last_created_local_surface_id.child_sequence_number(); + // Neither sequence numbers of the LocalSurfaceId can decrease and at least // one must increase. bool monotonically_increasing_id = @@ -401,8 +409,7 @@ last_created_local_surface_id.child_sequence_number()) && (local_surface_id.parent_sequence_number() > last_created_local_surface_id.parent_sequence_number() || - local_surface_id.child_sequence_number() > - last_created_local_surface_id.child_sequence_number()); + child_initiated_synchronization_event); if (!surface_info.is_valid() || !monotonically_increasing_id) { TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation", @@ -410,7 +417,15 @@ return SubmitResult::SURFACE_INVARIANTS_VIOLATION; } - current_surface = CreateSurface(surface_info); + // If the last Surface doesn't have a dependent frame, and this frame + // corresponds to a child-initiated synchronization event then defer this + // Surface until a dependent frame arrives. This throttles child submission + // of CompositorFrames to the parent's embedding rate. + const bool block_activation_on_parent = + child_initiated_synchronization_event && + !last_surface_has_dependent_frame; + + current_surface = CreateSurface(surface_info, block_activation_on_parent); last_created_surface_id_ = SurfaceId(frame_sink_id_, local_surface_id); surface_manager_->SurfaceDamageExpected(current_surface->surface_id(), last_begin_frame_args_); @@ -564,10 +579,12 @@ } Surface* CompositorFrameSinkSupport::CreateSurface( - const SurfaceInfo& surface_info) { + const SurfaceInfo& surface_info, + bool block_activation_on_parent) { return surface_manager_->CreateSurface( weak_factory_.GetWeakPtr(), surface_info, - frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_); + frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_, + block_activation_on_parent); } SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h index 7a77470..cd7f6ea 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -197,7 +197,8 @@ bool WantsAnimateOnlyBeginFrames() const override; void UpdateNeedsBeginFramesInternal(); - Surface* CreateSurface(const SurfaceInfo& surface_info); + Surface* CreateSurface(const SurfaceInfo& surface_info, + bool block_activation_on_parent); // For the sync API calls, if we are blocking a client callback, runs it once // BeginFrame and FrameAck are done.
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc index a0d62a0..360e6a2e 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -554,26 +554,64 @@ LocalSurfaceId local_surface_id4(5, 3, kArbitraryToken); LocalSurfaceId local_surface_id5(8, 1, kArbitraryToken); LocalSurfaceId local_surface_id6(9, 3, kArbitraryToken); + + // LocalSurfaceId1(6, 1) auto result = support->MaybeSubmitCompositorFrame( local_surface_id1, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::ACCEPTED, result); + + // LocalSurfaceId(6, 2): Child-initiated synchronization. result = support->MaybeSubmitCompositorFrame( local_surface_id2, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::ACCEPTED, result); + + // Since the Surface corresponding to |local_surface_id1| was not a dependency + // anywhere then the Surface corresponding to |local_surface_id2| will not + // activate until it becomes a dependency. + Surface* last_created_surface = support->GetLastCreatedSurfaceForTesting(); + EXPECT_EQ(local_surface_id2, + last_created_surface->surface_id().local_surface_id()); + EXPECT_FALSE(last_created_surface->HasActiveFrame()); + + SurfaceId surface_id2(kAnotherArbitraryFrameSinkId, local_surface_id2); + auto frame = + CompositorFrameBuilder() + .AddDefaultRenderPass() + .SetActivationDependencies({surface_id2}) + .SetReferencedSurfaces({SurfaceRange(base::nullopt, surface_id2)}) + .Build(); + result = support_->MaybeSubmitCompositorFrame( + local_surface_id_, std::move(frame), base::nullopt, 0, + mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); + EXPECT_EQ(SubmitResult::ACCEPTED, result); + + // Submitting a CompositorFrame to the parent FrameSink with a dependency on + // |local_surface_id2| causes that Surface's CompositorFrame to activate. + EXPECT_TRUE(last_created_surface->HasActiveFrame()); + + // LocalSurfaceId(7, 2): Parent-initiated synchronization. result = support->MaybeSubmitCompositorFrame( local_surface_id3, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::ACCEPTED, result); + + // LocalSurfaceId(5, 3): Surface Invariants Violation. Not monotonically + // increasing. result = support->MaybeSubmitCompositorFrame( local_surface_id4, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); + + // LocalSurfaceId(8, 1): Surface Invariants Violation. Not monotonically + // increasing. result = support->MaybeSubmitCompositorFrame( local_surface_id5, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback()); EXPECT_EQ(SubmitResult::SURFACE_INVARIANTS_VIOLATION, result); + + // LocalSurfaceId(9, 3): Parent AND child-initiated synchronization. result = support->MaybeSubmitCompositorFrame( local_surface_id6, MakeDefaultCompositorFrame(), base::nullopt, 0, mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc index 8174fbf..b02f9b2 100644 --- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc +++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -2166,9 +2166,19 @@ // Verify that there is a temporary reference for child_id3. EXPECT_TRUE(HasTemporaryReference(child_id3)); + // The surface corresponding to |child_id3| will not be activated until + // a parent embeds it because it's a child initiated synchronization. + EXPECT_EQ(GetSurfaceForId(child_id2), + GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); + + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, + std::vector<TransferableResource>())); + EXPECT_EQ(GetSurfaceForId(child_id3), GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); - EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); + EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id3)); // If the primary surface is active, we return it. EXPECT_EQ(GetSurfaceForId(child_id3), @@ -2780,5 +2790,204 @@ EXPECT_EQ(parent_id, parent_support().last_activated_surface_id()); } +// If a parent CompositorFrame embeds a child Surface newer than the throttled +// child then the throttled child surface is immediately unthrottled. This +// unblocks the child to make progress to catch up with the parent. +TEST_F(SurfaceSynchronizationTest, + ParentEmbeddingFutureChildUnblocksCurrentChildSurface) { + SetFrameSinkHierarchy(kParentFrameSink, kChildFrameSink1); + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); + const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3); + + // |child_id1| Surface should immediately activate. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeDefaultCompositorFrame()); + Surface* child_surface1 = GetSurfaceForId(child_id1); + ASSERT_NE(nullptr, child_surface1); + EXPECT_FALSE(child_surface1->HasPendingFrame()); + EXPECT_TRUE(child_surface1->HasActiveFrame()); + + // |child_id2| Surface should not activate because |child_id1| was never + // added as a dependency by a parent. + child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), + MakeDefaultCompositorFrame()); + Surface* child_surface2 = GetSurfaceForId(child_id2); + ASSERT_NE(nullptr, child_surface2); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + + // The parent finally embeds a child surface that hasn't arrived which + // activates |child_id2|'s Surface in order for the child to make forward + // progress. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + + EXPECT_FALSE(child_surface2->HasPendingFrame()); + EXPECT_TRUE(child_surface2->HasActiveFrame()); +} + +// A child surface can be blocked on its own activation dependencies and on a +// parent embedding it as an activation dependency. Even if a child's activation +// dependencies arrive, it may not activate until it is embedded. +TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); + const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); + + // |child_id1| Surface should immediately activate. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeDefaultCompositorFrame()); + Surface* child_surface1 = GetSurfaceForId(child_id1); + ASSERT_NE(nullptr, child_surface1); + EXPECT_FALSE(child_surface1->HasPendingFrame()); + EXPECT_TRUE(child_surface1->HasActiveFrame()); + + // |child_id2| Surface should not activate because |child_id1| was never + // added as a dependency by a parent AND it depends on |child_id3| which has + // not yet arrived. + child_support1().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + Surface* child_surface2 = GetSurfaceForId(child_id2); + ASSERT_NE(nullptr, child_surface2); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + EXPECT_THAT(child_surface2->activation_dependencies(), + UnorderedElementsAre(child_id3)); + + // |child_id2|'s dependency has arrived but |child_id2| Surface has not + // activated because it is still throttled by its parent. It will not + // activate until its parent arrives. + child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), + MakeDefaultCompositorFrame()); + EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + + // The parent finally embeds a |child_id2| which activates |child_id2|'s + // Surface. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + + EXPECT_FALSE(child_surface2->HasPendingFrame()); + EXPECT_TRUE(child_surface2->HasActiveFrame()); +} + +// Similar to the previous test, a child surface can be blocked on its own +// activation dependencies and on a parent embedding it as an activation +// dependency. In this case, the parent CompositorFrame arrives first, and then +// the activation dependency. +TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent2) { + const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); + const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); + + // |child_id1| Surface should immediately activate. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeDefaultCompositorFrame()); + Surface* child_surface1 = GetSurfaceForId(child_id1); + ASSERT_NE(nullptr, child_surface1); + EXPECT_FALSE(child_surface1->HasPendingFrame()); + EXPECT_TRUE(child_surface1->HasActiveFrame()); + + // |child_id2| Surface should not activate because |child_id1| was never + // added as a dependency by a parent AND it depends on |child_id3| which has + // not yet arrived. + child_support1().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + Surface* child_surface2 = GetSurfaceForId(child_id2); + ASSERT_NE(nullptr, child_surface2); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + EXPECT_THAT(child_surface2->activation_dependencies(), + UnorderedElementsAre(child_id3)); + EXPECT_FALSE(child_surface2->HasDependentFrame()); + + // The parent embeds |child_id2| but |child_id2|'s Surface cannot activate + // until its dependencies arrive. + parent_support().SubmitCompositorFrame( + parent_id.local_surface_id(), + MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + EXPECT_THAT(child_surface2->activation_dependencies(), + UnorderedElementsAre(child_id3)); + EXPECT_TRUE(child_surface2->HasDependentFrame()); + + // |child_id2|'s dependency has arrived and |child_id2|'s Surface finally + // activates. + child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), + MakeDefaultCompositorFrame()); + EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); + EXPECT_FALSE(child_surface2->HasPendingFrame()); + EXPECT_TRUE(child_surface2->HasActiveFrame()); +} + +// This test verifies that if a child-initiated synchronization is blocked +// on a parent, then the frame will still activate after a deadline passes. +TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentActivatesAfterDeadline) { + const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); + const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); + + // |child_id1| Surface should immediately activate. + child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), + MakeDefaultCompositorFrame()); + Surface* child_surface1 = GetSurfaceForId(child_id1); + ASSERT_NE(nullptr, child_surface1); + EXPECT_FALSE(child_surface1->HasPendingFrame()); + EXPECT_TRUE(child_surface1->HasActiveFrame()); + EXPECT_FALSE(child_surface1->HasDependentFrame()); + EXPECT_FALSE(child_surface1->has_deadline()); + + // |child_id2| Surface should not activate because |child_id1| was never + // added as a dependency by a parent. + child_support1().SubmitCompositorFrame( + child_id2.local_surface_id(), + MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), + std::vector<TransferableResource>(), + MakeDefaultDeadline())); + Surface* child_surface2 = GetSurfaceForId(child_id2); + ASSERT_NE(nullptr, child_surface2); + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + EXPECT_FALSE(child_surface2->HasDependentFrame()); + EXPECT_TRUE(child_surface2->has_deadline()); + + for (int i = 0; i < 3; ++i) { + SendNextBeginFrame(); + // There is still a looming deadline! Eeek! + EXPECT_TRUE(child_surface2->HasPendingFrame()); + EXPECT_FALSE(child_surface2->HasActiveFrame()); + EXPECT_FALSE(child_surface2->HasDependentFrame()); + EXPECT_TRUE(child_surface2->has_deadline()); + } + + SendNextBeginFrame(); + + EXPECT_FALSE(child_surface2->HasPendingFrame()); + EXPECT_TRUE(child_surface2->HasActiveFrame()); + // We pretend this is true so that subsequent CompositorFrames to the same + // surface do not block on the parent again. + EXPECT_TRUE(child_surface2->HasDependentFrame()); + EXPECT_FALSE(child_surface2->has_deadline()); +} + } // namespace test } // namespace viz
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc index f279157..5870483 100644 --- a/components/viz/service/surfaces/surface.cc +++ b/components/viz/service/surfaces/surface.cc
@@ -26,11 +26,13 @@ Surface::Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, base::WeakPtr<SurfaceClient> surface_client, - bool needs_sync_tokens) + bool needs_sync_tokens, + bool block_activation_on_parent) : surface_info_(surface_info), surface_manager_(surface_manager), surface_client_(std::move(surface_client)), - needs_sync_tokens_(needs_sync_tokens) { + needs_sync_tokens_(needs_sync_tokens), + block_activation_on_parent_(block_activation_on_parent) { TRACE_EVENT_ASYNC_BEGIN1(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"), "Surface", this, "surface_info", surface_info.ToString()); @@ -186,6 +188,20 @@ } } +void Surface::OnSurfaceDependencyAdded() { + if (seen_first_surface_dependency_) + return; + + seen_first_surface_dependency_ = true; + if (!activation_dependencies_.empty() || !pending_frame_data_) + return; + + DCHECK(frame_sink_id_dependencies_.empty()); + + // All blockers have been cleared. The surface can be activated now. + ActivatePendingFrame(base::nullopt); +} + void Surface::Close() { closed_ = true; } @@ -222,7 +238,16 @@ // regardless of whether it's pending or active. surface_client_->ReceiveFromChild(frame.resource_list); - if (activation_dependencies_.empty()) { + if (!seen_first_surface_dependency_) { + seen_first_surface_dependency_ = + surface_manager_->dependency_tracker()->HasSurfaceBlockedOn( + surface_id()); + } + + bool block_activation = + block_activation_on_parent_ && !seen_first_surface_dependency_; + + if (!block_activation && activation_dependencies_.empty()) { // If there are no blockers, then immediately activate the frame. ActivateFrame( FrameData(std::move(frame), frame_index, std::move(presented_callback)), @@ -298,7 +323,12 @@ dependency.local_surface_id() <= surface_id.local_surface_id(); }); - if (!activation_dependencies_.empty()) + // We cannot activate this CompositorFrame if there are still missing + // activation dependencies or this surface is blocked on its parent arriving + // and the parent has not arrived yet. + bool block_activation = + block_activation_on_parent_ && !seen_first_surface_dependency_; + if (block_activation || !activation_dependencies_.empty()) return; DCHECK(frame_sink_id_dependencies_.empty()); @@ -307,6 +337,17 @@ ActivatePendingFrame(base::nullopt); } +bool Surface::IsBlockedOn(const SurfaceId& surface_id) const { + for (const SurfaceId& dependency : activation_dependencies_) { + if (dependency.frame_sink_id() != surface_id.frame_sink_id()) + continue; + + if (dependency.local_surface_id() <= surface_id.local_surface_id()) + return true; + } + return false; +} + void Surface::ActivatePendingFrameForDeadline( base::Optional<base::TimeDelta> duration) { if (!pending_frame_data_) @@ -483,7 +524,12 @@ for (const SurfaceId& surface_id : current_frame.metadata.activation_dependencies) { + // Inform the Surface |dependency| that it's been added as a dependency in + // another Surface's CompositorFrame. + surface_manager_->SurfaceDependencyAdded(surface_id); + Surface* dependency = surface_manager_->GetSurfaceForId(surface_id); + // If a activation dependency does not have a corresponding active frame in // the display compositor, then it blocks this frame. if (!dependency || !dependency->HasActiveFrame()) {
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h index 9f53af1..ba53c4dc 100644 --- a/components/viz/service/surfaces/surface.h +++ b/components/viz/service/surfaces/surface.h
@@ -81,7 +81,8 @@ Surface(const SurfaceInfo& surface_info, SurfaceManager* surface_manager, base::WeakPtr<SurfaceClient> surface_client, - bool needs_sync_tokens); + bool needs_sync_tokens, + bool block_activation_on_parent); ~Surface(); void SetDependencyDeadline( @@ -121,6 +122,10 @@ bool needs_sync_tokens() const { return needs_sync_tokens_; } + bool block_activation_on_parent() const { + return block_activation_on_parent_; + } + // Returns false if |frame| is invalid. // |frame_rejected_callback| will be called once if the frame will not be // displayed. @@ -136,6 +141,10 @@ // frame. void NotifySurfaceIdAvailable(const SurfaceId& surface_id); + // Returns whether the Surface is blocked on the provided |surface_id| or a + // predecessor. + bool IsBlockedOn(const SurfaceId& surface_id) const; + // Called if a deadline has been hit and this surface is not yet active but // it's marked as respecting deadlines. void ActivatePendingFrameForDeadline( @@ -200,6 +209,10 @@ return HasActiveFrame() && !active_frame_data_->frame_processed; } + // Returns true if at any point, another Surface's CompositorFrame has + // depended on this Surface. + bool HasDependentFrame() const { return seen_first_surface_dependency_; } + // SurfaceDeadlineClient implementation: void OnDeadline(base::TimeDelta duration) override; @@ -210,6 +223,9 @@ // referenced SurfaceRange. void OnChildActivated(const SurfaceId& surface_id); + // Called when this surface is embedded by another Surface's CompositorFrame. + void OnSurfaceDependencyAdded(); + private: struct SequenceNumbers { uint32_t parent_sequence_number = 0u; @@ -296,7 +312,9 @@ bool closed_ = false; bool seen_first_frame_activation_ = false; bool seen_first_surface_embedding_ = false; + bool seen_first_surface_dependency_ = false; const bool needs_sync_tokens_; + const bool block_activation_on_parent_; base::flat_set<SurfaceId> activation_dependencies_; base::flat_set<SurfaceId> late_activation_dependencies_;
diff --git a/components/viz/service/surfaces/surface_dependency_tracker.cc b/components/viz/service/surfaces/surface_dependency_tracker.cc index 12a6310..ed24b25 100644 --- a/components/viz/service/surfaces/surface_dependency_tracker.cc +++ b/components/viz/service/surfaces/surface_dependency_tracker.cc
@@ -34,15 +34,72 @@ } } + // If |surface| is blocking on the arrival of a parent and the parent frame + // has not yet arrived then track this |surface|'s SurfaceId by FrameSinkId so + // that if a parent refers to it or a more recent surface, then + // SurfaceDependencyTracker reports back that a dependency has been added. + if (surface->block_activation_on_parent() && !surface->HasDependentFrame()) { + surfaces_blocked_on_parent_by_frame_sink_id_[surface->surface_id() + .frame_sink_id()] + .insert(surface->surface_id()); + } + UpdateSurfaceDeadline(surface); } +bool SurfaceDependencyTracker::HasSurfaceBlockedOn( + const SurfaceId& surface_id) const { + auto it = blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id()); + if (it == blocked_surfaces_from_dependency_.end()) + return false; + + for (const SurfaceId& blocked_surface_by_id : it->second) { + Surface* blocked_surface = + surface_manager_->GetSurfaceForId(blocked_surface_by_id); + if (blocked_surface && blocked_surface->IsBlockedOn(surface_id)) + return true; + } + return false; +} + void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) { if (!surface->late_activation_dependencies().empty()) surfaces_with_missing_dependencies_.insert(surface->surface_id()); else surfaces_with_missing_dependencies_.erase(surface->surface_id()); NotifySurfaceIdAvailable(surface->surface_id()); + // We treat an activation (by deadline) as being the equivalent of a parent + // embedding the surface. + OnSurfaceDependencyAdded(surface->surface_id()); +} + +void SurfaceDependencyTracker::OnSurfaceDependencyAdded( + const SurfaceId& surface_id) { + auto it = surfaces_blocked_on_parent_by_frame_sink_id_.find( + surface_id.frame_sink_id()); + if (it == surfaces_blocked_on_parent_by_frame_sink_id_.end()) + return; + + std::vector<SurfaceId> dependencies_to_notify; + + base::flat_set<SurfaceId>& blocked_surfaces = it->second; + for (auto iter = blocked_surfaces.begin(); iter != blocked_surfaces.end();) { + if (iter->local_surface_id() <= surface_id.local_surface_id()) { + dependencies_to_notify.push_back(*iter); + iter = blocked_surfaces.erase(iter); + } else { + ++iter; + } + } + + if (blocked_surfaces.empty()) + surfaces_blocked_on_parent_by_frame_sink_id_.erase(it); + + for (const SurfaceId& dependency : dependencies_to_notify) { + Surface* surface = surface_manager_->GetSurfaceForId(dependency); + if (surface) + surface->OnSurfaceDependencyAdded(); + } } void SurfaceDependencyTracker::OnSurfaceDependenciesChanged( @@ -78,6 +135,7 @@ // Pretend that the discarded surface's SurfaceId is now available to // unblock dependencies because we now know the surface will never activate. NotifySurfaceIdAvailable(surface->surface_id()); + OnSurfaceDependencyAdded(surface->surface_id()); } void SurfaceDependencyTracker::OnFrameSinkInvalidated( @@ -85,6 +143,7 @@ // We now know the frame sink will never generated any more frames, // thus unblock all dependencies to any future surfaces. NotifySurfaceIdAvailable(SurfaceId::MaxSequenceId(frame_sink_id)); + OnSurfaceDependencyAdded(SurfaceId::MaxSequenceId(frame_sink_id)); } void SurfaceDependencyTracker::ActivateLateSurfaceSubtree(Surface* surface) {
diff --git a/components/viz/service/surfaces/surface_dependency_tracker.h b/components/viz/service/surfaces/surface_dependency_tracker.h index 064487b..8a217d2 100644 --- a/components/viz/service/surfaces/surface_dependency_tracker.h +++ b/components/viz/service/surfaces/surface_dependency_tracker.h
@@ -34,7 +34,12 @@ // informed when that surface's dependencies are resolved. void RequestSurfaceResolution(Surface* surface); + // Returns whether the dependency tracker has a surface blocked on the + // provided |surface_id|. + bool HasSurfaceBlockedOn(const SurfaceId& surface_id) const; + void OnSurfaceActivated(Surface* surface); + void OnSurfaceDependencyAdded(const SurfaceId& surface_id); void OnSurfaceDependenciesChanged( Surface* surface, const base::flat_set<FrameSinkId>& added_dependencies, @@ -68,6 +73,11 @@ std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash> blocked_surfaces_from_dependency_; + // A map from a FrameSinkid to a set of surfaces with that FrameSinkId that + // are blocked on a parent arriving to embed them. + std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash> + surfaces_blocked_on_parent_by_frame_sink_id_; + // The set of SurfaceIds corresponding to Surfaces that have active // CompositorFrames with missing dependencies. base::flat_set<SurfaceId> surfaces_with_missing_dependencies_;
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc index 89c5fd2d..f9fc14b 100644 --- a/components/viz/service/surfaces/surface_manager.cc +++ b/components/viz/service/surfaces/surface_manager.cc
@@ -106,7 +106,8 @@ base::WeakPtr<SurfaceClient> surface_client, const SurfaceInfo& surface_info, BeginFrameSource* begin_frame_source, - bool needs_sync_tokens) { + bool needs_sync_tokens, + bool block_activation_on_parent) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(surface_info.is_valid()); DCHECK(surface_client); @@ -116,7 +117,8 @@ auto it = surface_map_.find(surface_info.id()); if (it == surface_map_.end()) { std::unique_ptr<Surface> surface = std::make_unique<Surface>( - surface_info, this, surface_client, needs_sync_tokens); + surface_info, this, surface_client, needs_sync_tokens, + block_activation_on_parent); surface->SetDependencyDeadline(std::make_unique<SurfaceDependencyDeadline>( surface.get(), begin_frame_source, tick_clock_)); surface_map_[surface_info.id()] = std::move(surface); @@ -606,6 +608,10 @@ dependency_tracker_.OnSurfaceActivated(surface); } +void SurfaceManager::SurfaceDependencyAdded(const SurfaceId& surface_id) { + dependency_tracker_.OnSurfaceDependencyAdded(surface_id); +} + void SurfaceManager::SurfaceDependenciesChanged( Surface* surface, const base::flat_set<FrameSinkId>& added_dependencies,
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h index c976f6b..b562cc1 100644 --- a/components/viz/service/surfaces/surface_manager.h +++ b/components/viz/service/surfaces/surface_manager.h
@@ -85,11 +85,13 @@ Surface* CreateSurface(base::WeakPtr<SurfaceClient> surface_client, const SurfaceInfo& surface_info, BeginFrameSource* begin_frame_source, - bool needs_sync_tokens); + bool needs_sync_tokens, + bool block_activation_on_parent); // Destroy the Surface once a set of sequence numbers has been satisfied. void DestroySurface(const SurfaceId& surface_id); + // Returns a Surface corresponding to the provided |surface_id|. Surface* GetSurfaceForId(const SurfaceId& surface_id); void AddObserver(SurfaceObserver* obs) { observer_list_.AddObserver(obs); } @@ -123,6 +125,10 @@ void SurfaceActivated(Surface* surface, base::Optional<base::TimeDelta> duration); + // Called when this |surface_id| is referenced as an activation dependency + // from a parent CompositorFrame. + void SurfaceDependencyAdded(const SurfaceId& surface_id); + // Called when the dependencies of a pending CompositorFrame within |surface| // has changed. void SurfaceDependenciesChanged(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 0184681a..5bdaa0f1 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -148,6 +148,7 @@ #include "media/mojo/services/media_metrics_provider.h" #include "media/mojo/services/video_decode_perf_history.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/data_pipe.h" @@ -886,6 +887,31 @@ last_committed_origin_, std::move(default_factory_request)); } +void RenderFrameHostImpl::MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + std::vector<url::Origin> request_initiators, + bool push_to_renderer_now) { + size_t old_size = initiators_requiring_separate_url_loader_factory_.size(); + initiators_requiring_separate_url_loader_factory_.insert( + request_initiators.begin(), request_initiators.end()); + size_t new_size = initiators_requiring_separate_url_loader_factory_.size(); + bool insertion_took_place = (old_size != new_size); + if (push_to_renderer_now && insertion_took_place) + UpdateSubresourceLoaderFactories(); +} + +URLLoaderFactoryBundleInfo::OriginMap +RenderFrameHostImpl::CreateInitiatorSpecificURLLoaderFactories() { + URLLoaderFactoryBundleInfo::OriginMap result; + for (const url::Origin& initiator : + initiators_requiring_separate_url_loader_factory_) { + network::mojom::URLLoaderFactoryPtrInfo factory_info; + CreateNetworkServiceDefaultFactoryInternal( + initiator, mojo::MakeRequest(&factory_info)); + result[initiator] = std::move(factory_info); + } + return result; +} + gfx::NativeView RenderFrameHostImpl::GetNativeView() { RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView(); if (!view) @@ -4123,6 +4149,9 @@ subresource_loader_factories->scheme_specific_factory_infos().emplace( factory.first, std::move(factory_proxy_info)); } + + subresource_loader_factories->initiator_specific_factory_infos() = + CreateInitiatorSpecificURLLoaderFactories(); } // It is imperative that cross-document navigations always provide a set of @@ -4255,7 +4284,8 @@ origin, mojo::MakeRequest(&default_factory_info)); subresource_loader_factories = std::make_unique<URLLoaderFactoryBundleInfo>( std::move(default_factory_info), - URLLoaderFactoryBundleInfo::SchemeMap(), bypass_redirect_checks); + URLLoaderFactoryBundleInfo::SchemeMap(), + URLLoaderFactoryBundleInfo::OriginMap(), bypass_redirect_checks); } SaveSubresourceFactories(std::move(subresource_loader_factories)); @@ -4798,7 +4828,8 @@ std::unique_ptr<URLLoaderFactoryBundleInfo> subresource_loader_factories = std::make_unique<URLLoaderFactoryBundleInfo>( std::move(default_factory_info), - URLLoaderFactoryBundleInfo::SchemeMap(), bypass_redirect_checks); + URLLoaderFactoryBundleInfo::SchemeMap(), + CreateInitiatorSpecificURLLoaderFactories(), bypass_redirect_checks); SaveSubresourceFactories(std::move(subresource_loader_factories)); GetNavigationControl()->UpdateSubresourceLoaderFactories( CloneSubresourceFactories()); @@ -4843,14 +4874,6 @@ bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactoryInternal( const url::Origin& origin, network::mojom::URLLoaderFactoryRequest default_factory_request) { - network::mojom::URLLoaderFactoryParamsPtr params = - network::mojom::URLLoaderFactoryParams::New(); - params->process_id = GetProcess()->GetID(); - params->disable_web_security = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableWebSecurity); - SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get()); - auto* context = GetSiteInstance()->GetBrowserContext(); bool bypass_redirect_checks = false; @@ -4860,23 +4883,24 @@ &default_factory_request, &bypass_redirect_checks); } - // Keep DevTools proxy lasy, i.e. closest to the network. + // Keep DevTools proxy last, i.e. closest to the network. RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( this, false /* is_navigation */, false /* is_download */, &default_factory_request); - StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>( - BrowserContext::GetStoragePartition(context, GetSiteInstance())); + + // Create the URLLoaderFactory - either via ContentBrowserClient or ourselves. if (g_create_network_factory_callback_for_test.Get().is_null()) { - storage_partition->GetNetworkContext()->CreateURLLoaderFactory( - std::move(default_factory_request), std::move(params)); + GetProcess()->CreateURLLoaderFactory(origin, + std::move(default_factory_request)); } else { network::mojom::URLLoaderFactoryPtr original_factory; - storage_partition->GetNetworkContext()->CreateURLLoaderFactory( - mojo::MakeRequest(&original_factory), std::move(params)); + GetProcess()->CreateURLLoaderFactory(origin, + mojo::MakeRequest(&original_factory)); g_create_network_factory_callback_for_test.Get().Run( std::move(default_factory_request), GetProcess()->GetID(), original_factory.PassInterface()); } + return bypass_redirect_checks; }
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 2af671e..fe23b03 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -18,6 +18,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/containers/flat_set.h" #include "base/containers/id_map.h" #include "base/gtest_prod_util.h" #include "base/macros.h" @@ -252,6 +253,9 @@ const blink::WebMediaPlayerAction& action) override; bool CreateNetworkServiceDefaultFactory( network::mojom::URLLoaderFactoryRequest default_factory_request) override; + void MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + std::vector<url::Origin> request_initiators, + bool push_to_renderer_now) override; // IPC::Sender bool Send(IPC::Message* msg) override; @@ -1291,6 +1295,11 @@ std::unique_ptr<base::trace_event::TracedValue> CommitAsTracedValue( FrameHostMsg_DidCommitProvisionalLoad_Params* validated_params) const; + // Creates initiator-specific URLLoaderFactory objects for intiator origins + // registered via MarkInitiatorAsRequiringSeparateURLLoaderFactory method. + URLLoaderFactoryBundleInfo::OriginMap + CreateInitiatorSpecificURLLoaderFactories(); + // For now, RenderFrameHosts indirectly keep RenderViewHosts alive via a // refcount that calls Shutdown when it reaches zero. This allows each // RenderFrameHostManager to just care about RenderFrameHosts, while ensuring @@ -1701,6 +1710,11 @@ network::mojom::URLLoaderFactoryPtr network_service_connection_error_handler_holder_; + // Set of request-initiator-origins that require a separate URLLoaderFactory + // (e.g. for handling requests initiated by extension content scripts that + // require relaxed CORS/CORB rules). + base::flat_set<url::Origin> initiators_requiring_separate_url_loader_factory_; + // Holds the renderer generated ID and global request ID for the main frame // request. std::pair<int, GlobalRequestID> main_frame_request_ids_;
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc index 4718aa6..4a157e6 100644 --- a/content/browser/media/encrypted_media_browsertest.cc +++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -182,9 +182,7 @@ } void SetUpCommandLine(base::CommandLine* command_line) override { - command_line->AppendSwitchASCII( - switches::kAutoplayPolicy, - switches::autoplay::kNoUserGestureRequiredPolicy); + MediaBrowserTest::SetUpCommandLine(command_line); #if defined(SUPPORTS_EXTERNAL_CLEAR_KEY_IN_CONTENT_SHELL) scoped_feature_list_.InitWithFeatures({media::kExternalClearKeyForTesting}, {});
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc index 93bea4f..5aabc37 100644 --- a/content/browser/media/media_browsertest.cc +++ b/content/browser/media/media_browsertest.cc
@@ -30,6 +30,9 @@ command_line->AppendSwitchASCII( switches::kAutoplayPolicy, switches::autoplay::kNoUserGestureRequiredPolicy); + // Disable fallback after decode error to avoid unexpected test pass on the + // fallback path. + scoped_feature_list_.InitAndDisableFeature(media::kFallbackAfterDecodeError); } void MediaBrowserTest::RunMediaTestPage(const std::string& html_page,
diff --git a/content/browser/media/media_browsertest.h b/content/browser/media/media_browsertest.h index b8e0b906..bc9af5cb 100644 --- a/content/browser/media/media_browsertest.h +++ b/content/browser/media/media_browsertest.h
@@ -8,6 +8,7 @@ #include <string> #include "base/strings/string_split.h" +#include "base/test/scoped_feature_list.h" #include "content/public/test/content_browser_test.h" namespace content { @@ -44,6 +45,9 @@ // Adds titles that RunTest() should wait for. virtual void AddTitlesToAwait(content::TitleWatcher* title_watcher); + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; } // namespace content
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc index 81d0ffa..6dd5d98 100644 --- a/content/browser/media/media_internals.cc +++ b/content/browser/media/media_internals.cc
@@ -118,6 +118,10 @@ const char kAudioLogStatusKey[] = "status"; const char kAudioLogUpdateFunction[] = "media.updateAudioComponent"; +const char kAudioFocusFunction[] = "media.onReceiveAudioFocusState"; +const char kAudioFocusIdKey[] = "id"; +const char kAudioFocusSessionsKey[] = "sessions"; + } // namespace namespace content { @@ -716,30 +720,31 @@ void MediaInternals::SendAudioFocusState() { #if !defined(OS_ANDROID) + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!CanUpdate()) return; - base::DictionaryValue audio_focus_data; - const std::list<AudioFocusManager::StackRow>& stack = - AudioFocusManager::GetInstance()->audio_focus_stack_; + audio_focus_data_.Clear(); + + auto& stack = AudioFocusManager::GetInstance()->audio_focus_stack_; // We should go backwards through the stack so the top of the stack is always // shown first in the list. base::ListValue stack_data; for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { - MediaSessionImpl::DebugInfo debug_info = - (*iter).media_session->GetDebugInfo(); base::DictionaryValue media_session_data; - media_session_data.SetKey("name", base::Value(debug_info.name)); - media_session_data.SetKey("owner", base::Value(debug_info.owner)); - media_session_data.SetKey("state", base::Value(debug_info.state)); + media_session_data.SetKey(kAudioFocusIdKey, base::Value((*iter)->id())); stack_data.GetList().push_back(std::move(media_session_data)); + + (*iter)->session()->GetDebugInfo( + base::BindOnce(&MediaInternals::DidGetAudioFocusDebugInfo, + base::Unretained(this), (*iter)->id())); } - audio_focus_data.SetKey("sessions", std::move(stack_data)); + audio_focus_data_.SetKey(kAudioFocusSessionsKey, std::move(stack_data)); - SendUpdate( - SerializeUpdate("media.onReceiveAudioFocusState", &audio_focus_data)); + if (stack.empty()) + SendUpdate(SerializeUpdate(kAudioFocusFunction, &audio_focus_data_)); #endif // !defined(OS_ANDROID) } @@ -824,7 +829,7 @@ } void MediaInternals::OnFocusGained( - media_session::mojom::MediaSessionPtr media_session, + media_session::mojom::MediaSessionInfoPtr media_session, media_session::mojom::AudioFocusType type) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -834,7 +839,7 @@ } void MediaInternals::OnFocusLost( - media_session::mojom::MediaSessionPtr media_session) { + media_session::mojom::MediaSessionInfoPtr media_session) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, @@ -842,6 +847,35 @@ base::Unretained(this))); } +void MediaInternals::DidGetAudioFocusDebugInfo( + int id, + media_session::mojom::MediaSessionDebugInfoPtr info) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (!CanUpdate()) + return; + + base::Value* sessions_list = + audio_focus_data_.FindKey(kAudioFocusSessionsKey); + DCHECK(sessions_list); + + bool updated = false; + for (auto& session : sessions_list->GetList()) { + if (session.FindKey(kAudioFocusIdKey)->GetInt() != id) + continue; + + session.SetKey("name", base::Value(info->name)); + session.SetKey("owner", base::Value(info->owner)); + session.SetKey("state", base::Value(info->state)); + updated = true; + } + + if (!updated) + return; + + SendUpdate(SerializeUpdate(kAudioFocusFunction, &audio_focus_data_)); +} + void MediaInternals::SendUpdate(const base::string16& update) { // SendUpdate() may be called from any thread, but must run on the UI thread. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
diff --git a/content/browser/media/media_internals.h b/content/browser/media/media_internals.h index 7aa7ddc..deeda062 100644 --- a/content/browser/media/media_internals.h +++ b/content/browser/media/media_internals.h
@@ -121,10 +121,15 @@ MediaInternals(); // AudioFocusObserver implementation. - void OnFocusGained(media_session::mojom::MediaSessionPtr media_session, + void OnFocusGained(media_session::mojom::MediaSessionInfoPtr media_session, media_session::mojom::AudioFocusType type) override; void OnFocusLost( - media_session::mojom::MediaSessionPtr media_session) override; + media_session::mojom::MediaSessionInfoPtr media_session) override; + + // Called when we receive audio focus debug info to display. + void DidGetAudioFocusDebugInfo( + int id, + media_session::mojom::MediaSessionDebugInfoPtr info); // Sends |update| to each registered UpdateCallback. Safe to call from any // thread, but will forward to the IO thread. @@ -163,6 +168,9 @@ NotificationRegistrar registrar_; + // Must only be accessed on the UI thread. + base::DictionaryValue audio_focus_data_; + // All variables below must be accessed under |lock_|. base::Lock lock_; bool can_update_;
diff --git a/content/browser/media/media_internals_proxy.cc b/content/browser/media/media_internals_proxy.cc index 0699f6e0..2b572f8b 100644 --- a/content/browser/media/media_internals_proxy.cc +++ b/content/browser/media/media_internals_proxy.cc
@@ -40,6 +40,10 @@ MediaInternals::GetInstance()->SendHistoricalMediaEvents(); +#if !defined(OS_ANDROID) + MediaInternals::GetInstance()->SendAudioFocusState(); +#endif + // Ask MediaInternals for its data on IO thread. base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, @@ -51,10 +55,6 @@ // TODO(xhwang): Investigate whether we can update on UI thread directly. MediaInternals::GetInstance()->SendAudioStreamData(); MediaInternals::GetInstance()->SendVideoCaptureDeviceCapabilities(); - -#if !defined(OS_ANDROID) - MediaInternals::GetInstance()->SendAudioFocusState(); -#endif } void MediaInternalsProxy::UpdateUIOnUIThread(const base::string16& update) {
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc index 033385ac..8374c25 100644 --- a/content/browser/media/media_internals_unittest.cc +++ b/content/browser/media/media_internals_unittest.cc
@@ -46,7 +46,7 @@ protected: // Extracts and deserializes the JSON update data; merges into |update_data_|. - void UpdateCallbackImpl(const base::string16& update) { + virtual void UpdateCallbackImpl(const base::string16& update) { // Each update string looks like "<JavaScript Function Name>({<JSON>});" // or for video capabilities: "<JavaScript Function Name>([{<JSON>}]);". // In the second case we will be able to extract the dictionary if it is the @@ -312,6 +312,8 @@ scoped_command_line_.GetProcessCommandLine()->AppendSwitch( media_session::switches::kEnableAudioFocus); + run_loop_ = std::make_unique<base::RunLoop>(); + content::MediaInternals::GetInstance()->AddUpdateCallback(update_cb_); browser_context_.reset(new TestBrowserContext()); } @@ -322,10 +324,21 @@ } protected: - void ExpectValue(base::ListValue expected_list) { + void UpdateCallbackImpl(const base::string16& update) override { + MediaInternalsTestBase::UpdateCallbackImpl(update); + run_loop_->Quit(); + } + + void ExpectValueAndReset(base::ListValue expected_list) { base::DictionaryValue expected_data; expected_data.SetKey("sessions", std::move(expected_list)); EXPECT_EQ(expected_data, update_data_); + Reset(); + } + + void Reset() { + update_data_.Clear(); + run_loop_ = std::make_unique<base::RunLoop>(); } std::unique_ptr<TestWebContents> CreateWebContents() { @@ -344,13 +357,16 @@ } void WaitForCallback() { - AudioFocusManager::GetInstance()->FlushForTesting(); - base::RunLoop().RunUntilIdle(); + if (!update_data_.empty()) + return; + + run_loop_->Run(); } MediaInternals::UpdateCallback update_cb_; private: + std::unique_ptr<base::RunLoop> run_loop_; base::test::ScopedCommandLine scoped_command_line_; std::unique_ptr<TestBrowserContext> browser_context_; }; @@ -366,13 +382,14 @@ // Check JSON is what we expect. { base::DictionaryValue expected_session; + expected_session.SetKey("id", base::Value(0)); expected_session.SetKey("name", GetAddressAsValue(media_session1)); expected_session.SetKey("owner", base::Value(kTestTitle1)); expected_session.SetKey("state", base::Value("Active")); base::ListValue expected_list; expected_list.GetList().push_back(std::move(expected_session)); - ExpectValue(std::move(expected_list)); + ExpectValueAndReset(std::move(expected_list)); } // Create another media session. @@ -382,15 +399,19 @@ media_session2->RequestSystemAudioFocus( AudioFocusType::kGainTransientMayDuck); WaitForCallback(); + Reset(); + WaitForCallback(); // Check JSON is what we expect. { base::DictionaryValue expected_session1; + expected_session1.SetKey("id", base::Value(1)); expected_session1.SetKey("name", GetAddressAsValue(media_session2)); expected_session1.SetKey("owner", base::Value(kTestTitle2)); expected_session1.SetKey("state", base::Value("Active")); base::DictionaryValue expected_session2; + expected_session2.SetKey("id", base::Value(0)); expected_session2.SetKey("name", GetAddressAsValue(media_session1)); expected_session2.SetKey("owner", base::Value(kTestTitle1)); expected_session2.SetKey("state", base::Value("Active Ducked")); @@ -398,7 +419,7 @@ base::ListValue expected_list; expected_list.GetList().push_back(std::move(expected_session1)); expected_list.GetList().push_back(std::move(expected_session2)); - ExpectValue(std::move(expected_list)); + ExpectValueAndReset(std::move(expected_list)); } // Abandon audio focus. @@ -408,13 +429,14 @@ // Check JSON is what we expect. { base::DictionaryValue expected_session; + expected_session.SetKey("id", base::Value(0)); expected_session.SetKey("name", GetAddressAsValue(media_session1)); expected_session.SetKey("owner", base::Value(kTestTitle1)); expected_session.SetKey("state", base::Value("Active")); base::ListValue expected_list; expected_list.GetList().push_back(std::move(expected_session)); - ExpectValue(std::move(expected_list)); + ExpectValueAndReset(std::move(expected_list)); } // Abandon audio focus. @@ -424,7 +446,7 @@ // Check JSON is what we expect. { base::ListValue expected_list; - ExpectValue(std::move(expected_list)); + ExpectValueAndReset(std::move(expected_list)); } }
diff --git a/content/browser/media/media_source_browsertest.cc b/content/browser/media/media_source_browsertest.cc index c03bf22..593ae9b 100644 --- a/content/browser/media/media_source_browsertest.cc +++ b/content/browser/media/media_source_browsertest.cc
@@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/command_line.h" -#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "content/browser/media/media_browsertest.h" #include "media/base/media_switches.h" @@ -37,7 +36,7 @@ namespace content { -class MediaSourceTest : public content::MediaBrowserTest { +class MediaSourceTest : public MediaBrowserTest { public: void TestSimplePlayback(const std::string& media_file, const std::string& media_type, @@ -48,15 +47,6 @@ RunMediaTestPage("media_source_player.html", query_params, expectation, false); } - - void SetUpCommandLine(base::CommandLine* command_line) override { - command_line->AppendSwitchASCII( - switches::kAutoplayPolicy, - switches::autoplay::kNoUserGestureRequiredPolicy); - } - - protected: - base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_F(MediaSourceTest, Playback_VideoAudio_WebM) {
diff --git a/content/browser/media/media_suspend_browsertest.cc b/content/browser/media/media_suspend_browsertest.cc index d005969..1cd1a66f 100644 --- a/content/browser/media/media_suspend_browsertest.cc +++ b/content/browser/media/media_suspend_browsertest.cc
@@ -24,13 +24,13 @@ // and that players suspended in this way can be resumed. Note: This does not // test suspend in various ready states; those tests are handled by layout tests // for ease of writing and ready state manipulation. -class MediaSuspendTest : public content::MediaBrowserTest { +class MediaSuspendTest : public MediaBrowserTest { public: void RunSuspendTest(const std::string& load_until) { base::StringPairs query_params; query_params.emplace_back("event", load_until); - GURL gurl = content::GetFileUrlWithQuery( + GURL gurl = GetFileUrlWithQuery( media::GetTestDataFilePath("media_suspend_test.html"), media::GetURLQueryString(query_params)); @@ -74,7 +74,7 @@ protected: void SetUpCommandLine(base::CommandLine* command_line) override { - content::MediaBrowserTest::SetUpCommandLine(command_line); + MediaBrowserTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kExposeInternalsForTesting); } };
diff --git a/content/browser/media/session/audio_focus_delegate.h b/content/browser/media/session/audio_focus_delegate.h index ba23f16..1f163c7 100644 --- a/content/browser/media/session/audio_focus_delegate.h +++ b/content/browser/media/session/audio_focus_delegate.h
@@ -27,12 +27,19 @@ virtual ~AudioFocusDelegate() = default; - virtual bool RequestAudioFocus( + enum class AudioFocusResult { + kSuccess, + kFailed, + kDelayed, + }; + + virtual AudioFocusResult RequestAudioFocus( media_session::mojom::AudioFocusType audio_focus_type) = 0; virtual void AbandonAudioFocus() = 0; // Retrieves the current |AudioFocusType| for the associated |MediaSession|. - virtual media_session::mojom::AudioFocusType GetCurrentFocusType() const = 0; + virtual base::Optional<media_session::mojom::AudioFocusType> + GetCurrentFocusType() const = 0; }; } // namespace content
diff --git a/content/browser/media/session/audio_focus_delegate_android.cc b/content/browser/media/session/audio_focus_delegate_android.cc index 59353a0..bb4e9cc 100644 --- a/content/browser/media/session/audio_focus_delegate_android.cc +++ b/content/browser/media/session/audio_focus_delegate_android.cc
@@ -30,14 +30,17 @@ Java_AudioFocusDelegate_create(env, reinterpret_cast<intptr_t>(this))); } -bool AudioFocusDelegateAndroid::RequestAudioFocus( +AudioFocusDelegate::AudioFocusResult +AudioFocusDelegateAndroid::RequestAudioFocus( media_session::mojom::AudioFocusType audio_focus_type) { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); - return Java_AudioFocusDelegate_requestAudioFocus( + bool success = Java_AudioFocusDelegate_requestAudioFocus( env, j_media_session_delegate_, audio_focus_type == media_session::mojom::AudioFocusType::kGainTransientMayDuck); + return success ? AudioFocusDelegate::AudioFocusResult::kSuccess + : AudioFocusDelegate::AudioFocusResult::kFailed; } void AudioFocusDelegateAndroid::AbandonAudioFocus() { @@ -46,7 +49,7 @@ Java_AudioFocusDelegate_abandonAudioFocus(env, j_media_session_delegate_); } -media_session::mojom::AudioFocusType +base::Optional<media_session::mojom::AudioFocusType> AudioFocusDelegateAndroid::GetCurrentFocusType() const { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env);
diff --git a/content/browser/media/session/audio_focus_delegate_android.h b/content/browser/media/session/audio_focus_delegate_android.h index 8c18679..a8cd35c 100644 --- a/content/browser/media/session/audio_focus_delegate_android.h +++ b/content/browser/media/session/audio_focus_delegate_android.h
@@ -27,10 +27,11 @@ void Initialize(); - bool RequestAudioFocus( + AudioFocusResult RequestAudioFocus( media_session::mojom::AudioFocusType audio_focus_type) override; void AbandonAudioFocus() override; - media_session::mojom::AudioFocusType GetCurrentFocusType() const override; + base::Optional<media_session::mojom::AudioFocusType> GetCurrentFocusType() + const override; // Called when the Android system requests the MediaSession to be suspended. // Called by Java through JNI.
diff --git a/content/browser/media/session/audio_focus_delegate_default.cc b/content/browser/media/session/audio_focus_delegate_default.cc index eb7ec76aa..65d9ea08 100644 --- a/content/browser/media/session/audio_focus_delegate_default.cc +++ b/content/browser/media/session/audio_focus_delegate_default.cc
@@ -5,6 +5,7 @@ #include "content/browser/media/session/audio_focus_delegate.h" #include "content/browser/media/session/audio_focus_manager.h" +#include "content/browser/media/session/media_session_impl.h" #include "services/media_session/public/cpp/switches.h" #include "services/media_session/public/mojom/audio_focus.mojom.h" @@ -22,16 +23,19 @@ ~AudioFocusDelegateDefault() override; // AudioFocusDelegate implementation. - bool RequestAudioFocus(AudioFocusType audio_focus_type) override; + AudioFocusResult RequestAudioFocus(AudioFocusType audio_focus_type) override; void AbandonAudioFocus() override; - AudioFocusType GetCurrentFocusType() const override; + base::Optional<AudioFocusType> GetCurrentFocusType() const override; private: + // Holds the current audio focus request id for |media_session_|. + base::Optional<AudioFocusManager::RequestId> request_id_; + // Weak pointer because |this| is owned by |media_session_|. MediaSessionImpl* media_session_; // The last requested AudioFocusType by the associated |media_session_|. - AudioFocusType audio_focus_type_if_disabled_; + base::Optional<AudioFocusType> audio_focus_type_if_disabled_; }; } // anonymous namespace @@ -42,29 +46,52 @@ AudioFocusDelegateDefault::~AudioFocusDelegateDefault() = default; -bool AudioFocusDelegateDefault::RequestAudioFocus( - AudioFocusType audio_focus_type) { +AudioFocusDelegate::AudioFocusResult +AudioFocusDelegateDefault::RequestAudioFocus(AudioFocusType audio_focus_type) { audio_focus_type_if_disabled_ = audio_focus_type; if (!media_session::IsAudioFocusEnabled()) - return true; + return AudioFocusDelegate::AudioFocusResult::kSuccess; - AudioFocusManager::GetInstance()->RequestAudioFocus(media_session_, - audio_focus_type); - return true; + media_session::mojom::MediaSessionInfoPtr session_info = + media_session_->GetMediaSessionInfoSync(); + + // Create a mojo interface pointer to our media session. This will allow the + // AudioFocusManager to interact with the media session across processes. + media_session::mojom::MediaSessionPtr media_session; + media_session_->BindToMojoRequest(mojo::MakeRequest(&media_session)); + + AudioFocusManager::RequestResponse response = + AudioFocusManager::GetInstance()->RequestAudioFocus( + std::move(media_session), std::move(session_info), audio_focus_type, + request_id_); + + request_id_ = response.first; + + return response.second ? AudioFocusDelegate::AudioFocusResult::kSuccess + : AudioFocusDelegate::AudioFocusResult::kFailed; } void AudioFocusDelegateDefault::AbandonAudioFocus() { - AudioFocusManager::GetInstance()->AbandonAudioFocus(media_session_); + audio_focus_type_if_disabled_.reset(); + + if (!request_id_.has_value()) + return; + + AudioFocusManager::GetInstance()->AbandonAudioFocus(request_id_.value()); + request_id_.reset(); } -AudioFocusType AudioFocusDelegateDefault::GetCurrentFocusType() const { - if (media_session::IsAudioFocusEnabled()) { - return AudioFocusManager::GetInstance()->GetFocusTypeForSession( - media_session_); - } +base::Optional<AudioFocusType> AudioFocusDelegateDefault::GetCurrentFocusType() + const { + if (!media_session::IsAudioFocusEnabled()) + return audio_focus_type_if_disabled_; - return audio_focus_type_if_disabled_; + if (!request_id_.has_value()) + return base::Optional<AudioFocusType>(); + + return AudioFocusManager::GetInstance()->GetFocusTypeForSession( + request_id_.value()); } // static
diff --git a/content/browser/media/session/audio_focus_delegate_default_browsertest.cc b/content/browser/media/session/audio_focus_delegate_default_browsertest.cc index 686e12a..13416af 100644 --- a/content/browser/media/session/audio_focus_delegate_default_browsertest.cc +++ b/content/browser/media/session/audio_focus_delegate_default_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "content/browser/media/session/audio_focus_test_util.h" #include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/mock_media_session_player_observer.h" #include "content/public/test/content_browser_test.h" @@ -23,21 +24,33 @@ player_observer(new MockMediaSessionPlayerObserver); MediaSessionImpl* media_session = MediaSessionImpl::Get(start_contents); - ASSERT_TRUE(media_session); + EXPECT_TRUE(media_session); MediaSessionImpl* other_media_session = MediaSessionImpl::Get(interrupt_contents); - ASSERT_TRUE(other_media_session); + EXPECT_TRUE(other_media_session); player_observer->StartNewPlayer(); - media_session->AddPlayer(player_observer.get(), 0, - media::MediaContentType::Persistent); + + { + test::TestAudioFocusObserver observer; + media_session->AddPlayer(player_observer.get(), 0, + media::MediaContentType::Persistent); + observer.WaitForGainedEvent(); + } + EXPECT_TRUE(media_session->IsActive()); EXPECT_FALSE(other_media_session->IsActive()); player_observer->StartNewPlayer(); - other_media_session->AddPlayer(player_observer.get(), 1, - media::MediaContentType::Persistent); + + { + test::TestAudioFocusObserver observer; + other_media_session->AddPlayer(player_observer.get(), 1, + media::MediaContentType::Persistent); + observer.WaitForGainedEvent(); + } + EXPECT_FALSE(media_session->IsActive()); EXPECT_TRUE(other_media_session->IsActive());
diff --git a/content/browser/media/session/audio_focus_manager.cc b/content/browser/media/session/audio_focus_manager.cc index 805ca47..c1671cb 100644 --- a/content/browser/media/session/audio_focus_manager.cc +++ b/content/browser/media/session/audio_focus_manager.cc
@@ -4,8 +4,12 @@ #include "content/browser/media/session/audio_focus_manager.h" +#include "base/atomic_sequence_num.h" +#include "base/memory/ptr_util.h" +#include "base/task/post_task.h" #include "content/browser/media/session/audio_focus_observer.h" #include "content/browser/media/session/media_session_impl.h" +#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "mojo/public/cpp/bindings/interface_request.h" @@ -14,14 +18,17 @@ namespace content { using media_session::mojom::AudioFocusType; +using media_session::mojom::MediaSessionPtr; +using media_session::mojom::MediaSessionInfo; +using media_session::mojom::MediaSessionInfoPtr; namespace { -media_session::mojom::MediaSessionPtr GetSessionMojoPtr( - MediaSessionImpl* session) { - media_session::mojom::MediaSessionPtr media_session_ptr; - session->BindToMojoRequest(mojo::MakeRequest(&media_session_ptr)); - return media_session_ptr; +// Generate a unique audio focus request ID for the audio focus request. The IDs +// are only handed out by the audio focus manager. +int GenerateAudioFocusRequestId() { + static base::AtomicSequenceNumber request_id; + return request_id.GetNext(); } } // namespace @@ -31,95 +38,104 @@ return base::Singleton<AudioFocusManager>::get(); } -void AudioFocusManager::RequestAudioFocus(MediaSessionImpl* media_session, - AudioFocusType type) { +AudioFocusManager::RequestResponse AudioFocusManager::RequestAudioFocus( + MediaSessionPtr media_session, + MediaSessionInfoPtr session_info, + AudioFocusType type, + base::Optional<RequestId> previous_id) { + DCHECK(!session_info.is_null()); + DCHECK(media_session.is_bound()); + if (!audio_focus_stack_.empty() && - audio_focus_stack_.back().media_session == media_session && - audio_focus_stack_.back().audio_focus_type == type && - audio_focus_stack_.back().media_session->IsActive()) { + previous_id == audio_focus_stack_.back()->id() && + audio_focus_stack_.back()->audio_focus_type() == type && + audio_focus_stack_.back()->IsActive()) { // Early returning if |media_session| is already on top (has focus) and is // active. - return; + return AudioFocusManager::RequestResponse(previous_id.value(), true); } - MaybeRemoveFocusEntry(media_session); + // If we have a previous ID then we should remove it from the stack. + if (previous_id.has_value()) + RemoveFocusEntryIfPresent(previous_id.value()); - // TODO(zqzhang): It seems like MediaSessionImpl is exposed to - // AudioFocusManager - // too much. Maybe it's better to do some abstraction and refactoring to clean - // up the relation between AudioFocusManager and MediaSessionImpl. - // See https://crbug.com/651069 if (type == AudioFocusType::kGainTransientMayDuck) { - for (auto& old_session : audio_focus_stack_) { - old_session.media_session->StartDucking(); - } + for (auto& old_session : audio_focus_stack_) + old_session->session()->StartDucking(); } else { for (auto& old_session : audio_focus_stack_) { - if (old_session.media_session->IsActive()) { - if (old_session.media_session->HasPepper()) - old_session.media_session->StartDucking(); - else - old_session.media_session->Suspend( - MediaSessionImpl::SuspendType::kSystem); + // If the session has the force duck bit set then we should duck the + // session instead of suspending it. + if (old_session->info()->force_duck) { + old_session->session()->StartDucking(); + } else { + old_session->session()->Suspend(MediaSession::SuspendType::kSystem); } } } - // Store the MediaSession and requested focus type. - audio_focus_stack_.emplace_back(media_session, type); - audio_focus_stack_.back().media_session->StopDucking(); + audio_focus_stack_.push_back(std::make_unique<AudioFocusManager::StackRow>( + std::move(media_session), std::move(session_info), type, + previous_id ? *previous_id : GenerateAudioFocusRequestId())); + + AudioFocusManager::StackRow* row = audio_focus_stack_.back().get(); + row->session()->StopDucking(); // Notify observers that we were gained audio focus. observers_.ForAllPtrs( - [media_session, - type](media_session::mojom::AudioFocusObserver* observer) { - observer->OnFocusGained(GetSessionMojoPtr(media_session), type); + [&row, type](media_session::mojom::AudioFocusObserver* observer) { + observer->OnFocusGained(row->info().Clone(), type); }); + + // We always grant the audio focus request but this may not always be the case + // in the future. + return AudioFocusManager::RequestResponse(row->id(), true); } -void AudioFocusManager::AbandonAudioFocus(MediaSessionImpl* media_session) { +void AudioFocusManager::AbandonAudioFocus(RequestId id) { if (audio_focus_stack_.empty()) return; - if (audio_focus_stack_.back().media_session != media_session) { - MaybeRemoveFocusEntry(media_session); + if (audio_focus_stack_.back()->id() != id) { + RemoveFocusEntryIfPresent(id); return; } + auto row = std::move(audio_focus_stack_.back()); audio_focus_stack_.pop_back(); + if (audio_focus_stack_.empty()) { // Notify observers that we lost audio focus. observers_.ForAllPtrs( - [media_session](media_session::mojom::AudioFocusObserver* observer) { - observer->OnFocusLost(GetSessionMojoPtr(media_session)); + [&row](media_session::mojom::AudioFocusObserver* observer) { + observer->OnFocusLost(row->info().Clone()); }); return; } - // Allow the top-most MediaSessionImpl having Pepper to unduck pepper even if - // it's - // not active. + // Allow the top-most MediaSession having force duck to unduck even if + // it is not active. for (auto iter = audio_focus_stack_.rbegin(); iter != audio_focus_stack_.rend(); ++iter) { - if (!iter->media_session->HasPepper()) + if (!(*iter)->info()->force_duck) continue; - MediaSessionImpl* pepper_session = iter->media_session; - AudioFocusType focus_type = iter->audio_focus_type; - pepper_session->StopDucking(); - MaybeRemoveFocusEntry(pepper_session); - audio_focus_stack_.emplace_back(pepper_session, focus_type); + auto duck_row = std::move(*iter); + duck_row->session()->StopDucking(); + audio_focus_stack_.erase(std::next(iter).base()); + audio_focus_stack_.push_back(std::move(duck_row)); return; } - // Only try to unduck the new MediaSessionImpl on top. The session might be - // still - // inactive but it will not be resumed (so it doesn't surprise the user). - audio_focus_stack_.back().media_session->StopDucking(); + + // Only try to unduck the new MediaSession on top. The session might be + // still inactive but it will not be resumed (so it doesn't surprise the + // user). + audio_focus_stack_.back()->session()->StopDucking(); // Notify observers that we lost audio focus. observers_.ForAllPtrs( - [media_session](media_session::mojom::AudioFocusObserver* observer) { - observer->OnFocusLost(GetSessionMojoPtr(media_session)); + [&row](media_session::mojom::AudioFocusObserver* observer) { + observer->OnFocusLost(row->info().Clone()); }); } @@ -128,11 +144,10 @@ return observers_.AddPtr(std::move(observer)); } -AudioFocusType AudioFocusManager::GetFocusTypeForSession( - MediaSessionImpl* media_session) const { - for (auto row : audio_focus_stack_) { - if (row.media_session == media_session) - return row.audio_focus_type; +AudioFocusType AudioFocusManager::GetFocusTypeForSession(RequestId id) { + for (auto& row : audio_focus_stack_) { + if (row->id() == id) + return row->audio_focus_type(); } NOTREACHED(); @@ -150,6 +165,9 @@ void AudioFocusManager::FlushForTesting() { observers_.FlushForTesting(); + + for (auto& session : audio_focus_stack_) + session->FlushForTesting(); } AudioFocusManager::AudioFocusManager() { @@ -160,14 +178,69 @@ AudioFocusManager::~AudioFocusManager() = default; -void AudioFocusManager::MaybeRemoveFocusEntry(MediaSessionImpl* media_session) { +void AudioFocusManager::RemoveFocusEntryIfPresent( + AudioFocusManager::RequestId id) { for (auto iter = audio_focus_stack_.begin(); iter != audio_focus_stack_.end(); ++iter) { - if (iter->media_session == media_session) { + if ((*iter)->id() == id) { audio_focus_stack_.erase(iter); break; } } } +AudioFocusManager::StackRow::StackRow(MediaSessionPtr session, + MediaSessionInfoPtr current_info, + AudioFocusType audio_focus_type, + AudioFocusManager::RequestId id) + : id_(id), + session_(std::move(session)), + current_info_(std::move(current_info)), + audio_focus_type_(audio_focus_type), + binding_(this) { + media_session::mojom::MediaSessionObserverPtr observer; + binding_.Bind(mojo::MakeRequest(&observer)); + + // Listen for mojo errors. + binding_.set_connection_error_handler(base::BindOnce( + &AudioFocusManager::StackRow::OnConnectionError, base::Unretained(this))); + session_.set_connection_error_handler(base::BindOnce( + &AudioFocusManager::StackRow::OnConnectionError, base::Unretained(this))); + + // Listen to info changes on the MediaSession. + session_->AddObserver(std::move(observer)); +}; + +AudioFocusManager::StackRow::~StackRow() = default; + +void AudioFocusManager::StackRow::MediaSessionInfoChanged( + MediaSessionInfoPtr info) { + current_info_ = std::move(info); +} + +media_session::mojom::MediaSession* AudioFocusManager::StackRow::session() { + return session_.get(); +} + +const media_session::mojom::MediaSessionInfoPtr& +AudioFocusManager::StackRow::info() const { + return current_info_; +} + +bool AudioFocusManager::StackRow::IsActive() const { + return current_info_->state == MediaSessionInfo::SessionState::kActive; +} + +void AudioFocusManager::StackRow::FlushForTesting() { + session_.FlushForTesting(); + binding_.FlushForTesting(); +} + +void AudioFocusManager::StackRow::OnConnectionError() { + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&AudioFocusManager::AbandonAudioFocus, + base::Unretained(AudioFocusManager::GetInstance()), id())); +} + } // namespace content
diff --git a/content/browser/media/session/audio_focus_manager.h b/content/browser/media/session/audio_focus_manager.h index 3bc2cc7..8871635 100644 --- a/content/browser/media/session/audio_focus_manager.h +++ b/content/browser/media/session/audio_focus_manager.h
@@ -11,25 +11,38 @@ #include "base/memory/singleton.h" #include "content/common/content_export.h" #include "content/public/browser/web_contents_observer.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr_set.h" #include "services/media_session/public/mojom/audio_focus.mojom.h" namespace content { -class MediaSessionImpl; - class CONTENT_EXPORT AudioFocusManager { public: + using RequestId = int; + using RequestResponse = std::pair<RequestId, bool>; + // Returns Chromium's internal AudioFocusManager. static AudioFocusManager* GetInstance(); - void RequestAudioFocus(MediaSessionImpl* media_session, - media_session::mojom::AudioFocusType type); + // Requests audio focus with |type| for the |media_session| with + // |session_info|. The function will return a |RequestResponse| which contains + // a |RequestId| and a boolean as to whether the request was successful. If a + // session wishes to request a different focus type it should provide its + // previous request id as |previous_id|. + RequestResponse RequestAudioFocus( + media_session::mojom::MediaSessionPtr media_session, + media_session::mojom::MediaSessionInfoPtr session_info, + media_session::mojom::AudioFocusType type, + base::Optional<RequestId> previous_id); - void AbandonAudioFocus(MediaSessionImpl* media_session); + // Abandons audio focus for a request with |id|. The next request on top of + // the stack will be granted audio focus. + void AbandonAudioFocus(RequestId id); - media_session::mojom::AudioFocusType GetFocusTypeForSession( - MediaSessionImpl* media_session) const; + // Returns the current audio focus type for a request with |id|. + media_session::mojom::AudioFocusType GetFocusTypeForSession(RequestId id); // Adds/removes audio focus observers. mojo::InterfacePtrSetElementId AddObserver( @@ -53,22 +66,57 @@ AudioFocusManager(); ~AudioFocusManager(); - void MaybeRemoveFocusEntry(MediaSessionImpl* media_session); + void RemoveFocusEntryIfPresent(RequestId id); // Weak reference of managed observers. Observers are expected to remove // themselves before being destroyed. mojo::InterfacePtrSet<media_session::mojom::AudioFocusObserver> observers_; - // Weak reference of managed MediaSessions and their requested focus type. - // A MediaSession must abandon audio focus before its destruction. - struct StackRow { - StackRow(MediaSessionImpl* media_session, - media_session::mojom::AudioFocusType audio_focus_type) - : media_session(media_session), audio_focus_type(audio_focus_type) {} - MediaSessionImpl* media_session; - media_session::mojom::AudioFocusType audio_focus_type; + // StackRow is a MediaSessionObserver and holds a cached copy of the latest + // MediaSessionInfo associated with the MediaSession. By keeping the info + // cached and readily available we can make audio focus decisions quickly + // without waiting on MediaSessions. + class StackRow : public media_session::mojom::MediaSessionObserver { + public: + StackRow(media_session::mojom::MediaSessionPtr session, + media_session::mojom::MediaSessionInfoPtr current_info, + media_session::mojom::AudioFocusType audio_focus_type, + RequestId id); + ~StackRow() override; + + // media_session::mojom::MediaSessionObserver. + void MediaSessionInfoChanged( + media_session::mojom::MediaSessionInfoPtr info) override; + + media_session::mojom::MediaSession* session(); + const media_session::mojom::MediaSessionInfoPtr& info() const; + media_session::mojom::AudioFocusType audio_focus_type() const { + return audio_focus_type_; + } + + bool IsActive() const; + int id() { return id_; } + + // Flush any pending mojo messages for testing. + void FlushForTesting(); + + private: + void OnConnectionError(); + + int id_; + media_session::mojom::MediaSessionPtr session_; + media_session::mojom::MediaSessionInfoPtr current_info_; + media_session::mojom::AudioFocusType audio_focus_type_; + mojo::Binding<media_session::mojom::MediaSessionObserver> binding_; + + DISALLOW_COPY_AND_ASSIGN(StackRow); }; - std::list<StackRow> audio_focus_stack_; + + // A stack of Mojo interface pointers and their requested audio focus type. + // A MediaSession must abandon audio focus before its destruction. + std::list<std::unique_ptr<StackRow>> audio_focus_stack_; + + DISALLOW_COPY_AND_ASSIGN(AudioFocusManager); }; } // namespace content
diff --git a/content/browser/media/session/audio_focus_manager_unittest.cc b/content/browser/media/session/audio_focus_manager_unittest.cc index fc4a597..da1040e5 100644 --- a/content/browser/media/session/audio_focus_manager_unittest.cc +++ b/content/browser/media/session/audio_focus_manager_unittest.cc
@@ -7,9 +7,7 @@ #include <memory> #include "base/command_line.h" -#include "base/optional.h" -#include "base/run_loop.h" -#include "content/browser/media/session/audio_focus_observer.h" +#include "content/browser/media/session/audio_focus_test_util.h" #include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_player_observer.h" #include "content/public/test/mock_render_process_host.h" @@ -17,51 +15,101 @@ #include "content/public/test/test_browser_thread_bundle.h" #include "content/test/test_web_contents.h" #include "media/base/media_content_type.h" +#include "mojo/public/cpp/bindings/binding_set.h" #include "services/media_session/public/cpp/switches.h" #include "services/media_session/public/mojom/audio_focus.mojom.h" namespace content { using media_session::mojom::AudioFocusType; +using media_session::mojom::MediaSessionInfo; +using media_session::mojom::MediaSessionInfoPtr; using media_session::mojom::MediaSessionPtr; namespace { -class MockAudioFocusObserver : public AudioFocusObserver { - public: - MockAudioFocusObserver() { RegisterAudioFocusObserver(); } +static const base::Optional<AudioFocusManager::RequestId> kNoRequestId; - void OnFocusGained(MediaSessionPtr session, AudioFocusType type) override { - EXPECT_TRUE(session.is_bound()); - focus_gained_call_ = type; - focus_lost_call_ = false; +static const AudioFocusManager::RequestId kNoFocusedSession = -1; + +class MockMediaSession : public media_session::mojom::MediaSession { + public: + MockMediaSession() = default; + explicit MockMediaSession(bool force_duck) : force_duck_(force_duck) {} + + ~MockMediaSession() override {} + + void Suspend(SuspendType suspend_type) override { + DCHECK_EQ(SuspendType::kSystem, suspend_type); + SetState(MediaSessionInfo::SessionState::kSuspended); } - void OnFocusLost(MediaSessionPtr session) override { - EXPECT_TRUE(session.is_bound()); - focus_lost_call_ = true; - focus_gained_call_.reset(); + void StartDucking() override { + is_ducking_ = true; + NotifyObservers(); } - base::Optional<AudioFocusType> focus_gained_call_; - bool focus_lost_call_ = false; -}; + void StopDucking() override { + is_ducking_ = false; + NotifyObservers(); + } -class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver { - public: - void OnSuspend(int player_id) override {} - void OnResume(int player_id) override {} - void OnSeekForward(int player_id, base::TimeDelta seek_time) override {} - void OnSeekBackward(int player_id, base::TimeDelta seek_time) override {} - void OnSetVolumeMultiplier( - int player_id, double volume_multiplier) override {} - RenderFrameHost* render_frame_host() const override { return nullptr; } + void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override { + std::move(callback).Run(GetSessionInfoSync()); + } + + void AddObserver( + media_session::mojom::MediaSessionObserverPtr observer) override { + observers_.AddPtr(std::move(observer)); + } + + void GetDebugInfo(GetDebugInfoCallback callback) override {} + + void BindToMojoRequest( + mojo::InterfaceRequest<media_session::mojom::MediaSession> request) { + bindings_.AddBinding(this, std::move(request)); + } + + void SetState(MediaSessionInfo::SessionState state) { + state_ = state; + NotifyObservers(); + } + + bool has_observers() const { return !observers_.empty(); } + + void CloseAllObservers() { observers_.CloseAll(); } + + private: + MediaSessionInfoPtr GetSessionInfoSync() { + MediaSessionInfoPtr info(MediaSessionInfo::New()); + info->force_duck = force_duck_; + info->state = state_; + if (is_ducking_) + info->state = MediaSessionInfo::SessionState::kDucking; + return info; + } + + void NotifyObservers() { + observers_.ForAllPtrs( + [this](media_session::mojom::MediaSessionObserver* observer) { + observer->MediaSessionInfoChanged(GetSessionInfoSync()); + }); + + // This will flush all pending async messages on the observers. + observers_.FlushForTesting(); + } + + const bool force_duck_ = false; + bool is_ducking_ = false; + MediaSessionInfo::SessionState state_ = + MediaSessionInfo::SessionState::kInactive; + + mojo::InterfacePtrSet<media_session::mojom::MediaSessionObserver> observers_; + mojo::BindingSet<media_session::mojom::MediaSession> bindings_; }; } // anonymous namespace -using SuspendType = MediaSession::SuspendType; - class AudioFocusManagerTest : public testing::Test { public: AudioFocusManagerTest() = default; @@ -73,7 +121,6 @@ RenderProcessHostImpl::set_render_process_host_factory_for_testing( rph_factory_.get()); browser_context_.reset(new TestBrowserContext()); - pepper_observer_.reset(new MockMediaSessionPlayerObserver()); // AudioFocusManager is a singleton so we should make sure we reset any // state in between tests. @@ -89,16 +136,16 @@ rph_factory_.reset(); } - MediaSessionImpl* GetAudioFocusedSession() const { + AudioFocusManager::RequestId GetAudioFocusedSession() const { const AudioFocusManager* manager = AudioFocusManager::GetInstance(); const auto& audio_focus_stack = manager->audio_focus_stack_; for (auto iter = audio_focus_stack.rbegin(); iter != audio_focus_stack.rend(); ++iter) { - if ((*iter).audio_focus_type == AudioFocusType::kGain) - return (*iter).media_session; + if ((*iter)->audio_focus_type() == AudioFocusType::kGain) + return (*iter)->id(); } - return nullptr; + return kNoFocusedSession; } int GetTransientMaybeDuckCount() const { @@ -108,7 +155,7 @@ for (auto iter = audio_focus_stack.rbegin(); iter != audio_focus_stack.rend(); ++iter) { - if ((*iter).audio_focus_type == AudioFocusType::kGainTransientMayDuck) + if ((*iter)->audio_focus_type() == AudioFocusType::kGainTransientMayDuck) ++count; else break; @@ -117,31 +164,47 @@ return count; } - double IsSessionDucking(MediaSessionImpl* session) { - return session->is_ducking_; // Quack! Quack! + void AbandonAudioFocus(AudioFocusManager::RequestId id) { + AudioFocusManager::GetInstance()->AbandonAudioFocus(id); + FlushForTesting(); } - void RequestAudioFocus(MediaSessionImpl* session, - AudioFocusType audio_focus_type) { - session->RequestSystemAudioFocus(audio_focus_type); + AudioFocusManager::RequestId RequestAudioFocus( + MockMediaSession* session, + AudioFocusType audio_focus_type) { + return RequestAudioFocus(session, audio_focus_type, kNoRequestId); } - void AbandonAudioFocus(MediaSessionImpl* session) { - session->AbandonSystemAudioFocusIfNeeded(); + AudioFocusManager::RequestId RequestAudioFocus( + MockMediaSession* session, + AudioFocusType audio_focus_type, + base::Optional<AudioFocusManager::RequestId> previous_id) { + media_session::mojom::MediaSessionPtr media_session; + session->BindToMojoRequest(mojo::MakeRequest(&media_session)); + + AudioFocusManager::RequestResponse response = + AudioFocusManager::GetInstance()->RequestAudioFocus( + std::move(media_session), test::GetMediaSessionInfoSync(session), + audio_focus_type, previous_id); + + // If the audio focus was granted then we should set the session state to + // active. + if (response.second) + session->SetState(MediaSessionInfo::SessionState::kActive); + + FlushForTesting(); + return response.first; } + MediaSessionInfo::SessionState GetState(MockMediaSession* session) { + return test::GetMediaSessionInfoSync(session)->state; + } + + private: void FlushForTesting() { AudioFocusManager::GetInstance()->FlushForTesting(); } - std::unique_ptr<WebContents> CreateWebContents() { - return TestWebContents::Create(browser_context_.get(), - SiteInstance::SiteInstance::Create(browser_context_.get())); - } - - std::unique_ptr<MediaSessionPlayerObserver> pepper_observer_; - - private: TestBrowserThreadBundle test_browser_thread_bundle_; std::unique_ptr<MockRenderProcessHostFactory> rph_factory_; @@ -150,400 +213,365 @@ TEST_F(AudioFocusManagerTest, InstanceAvailableAndSame) { AudioFocusManager* audio_focus_manager = AudioFocusManager::GetInstance(); - ASSERT_TRUE(!!audio_focus_manager); - ASSERT_EQ(audio_focus_manager, AudioFocusManager::GetInstance()); + EXPECT_TRUE(!!audio_focus_manager); + EXPECT_EQ(audio_focus_manager, AudioFocusManager::GetInstance()); +} + +TEST_F(AudioFocusManagerTest, AddObserverOnRequest) { + MockMediaSession media_session_1; + EXPECT_FALSE(media_session_1.has_observers()); + + RequestAudioFocus(&media_session_1, AudioFocusType::kGain, kNoRequestId); + EXPECT_TRUE(media_session_1.has_observers()); } TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_ReplaceFocusedEntry) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; + MockMediaSession media_session_3; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_1)); + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_2)); + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_3)); - std::unique_ptr<WebContents> web_contents_3(CreateWebContents()); - MediaSessionImpl* media_session_3 = - MediaSessionImpl::Get(web_contents_3.get()); + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_EQ(request_id_1, GetAudioFocusedSession()); + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id_2 = + RequestAudioFocus(&media_session_2, AudioFocusType::kGain); + EXPECT_EQ(request_id_2, GetAudioFocusedSession()); + EXPECT_EQ(MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_EQ(media_session_1, GetAudioFocusedSession()); - - RequestAudioFocus(media_session_2, AudioFocusType::kGain); - ASSERT_EQ(media_session_2, GetAudioFocusedSession()); - - RequestAudioFocus(media_session_3, AudioFocusType::kGain); - ASSERT_EQ(media_session_3, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id_3 = + RequestAudioFocus(&media_session_3, AudioFocusType::kGain); + EXPECT_EQ(request_id_3, GetAudioFocusedSession()); + EXPECT_EQ(MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); } TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_Duplicate) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; - ASSERT_EQ(nullptr, GetAudioFocusedSession()); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); + RequestAudioFocus(&media_session, AudioFocusType::kGain, request_id); + EXPECT_EQ(request_id, GetAudioFocusedSession()); } TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_FromTransient) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; - RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); + RequestAudioFocus(&media_session, AudioFocusType::kGain, request_id); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); } TEST_F(AudioFocusManagerTest, RequestAudioFocusTransient_FromGain) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); - RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session)); + RequestAudioFocus(&media_session, AudioFocusType::kGainTransientMayDuck, + request_id); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, GetState(&media_session)); } TEST_F(AudioFocusManagerTest, RequestAudioFocusTransient_FromGainWhileDucking) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + RequestAudioFocus(&media_session_2, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - RequestAudioFocus(media_session_1, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(2, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + RequestAudioFocus(&media_session_1, AudioFocusType::kGainTransientMayDuck, + request_id); + EXPECT_EQ(2, GetTransientMaybeDuckCount()); + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); } TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesFocusedEntry) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); - AbandonAudioFocus(media_session); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); + AbandonAudioFocus(request_id); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); } TEST_F(AudioFocusManagerTest, AbandonAudioFocus_NoAssociatedEntry) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); - - AbandonAudioFocus(media_session); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); + AbandonAudioFocus(kNoFocusedSession); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); } TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientEntry) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; - RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); { - MockAudioFocusObserver observer; - AbandonAudioFocus(media_session); - FlushForTesting(); + test::TestAudioFocusObserver observer; + AbandonAudioFocus(request_id); EXPECT_EQ(0, GetTransientMaybeDuckCount()); - EXPECT_TRUE(observer.focus_lost_call_); + EXPECT_TRUE(observer.focus_lost_session_.Equals( + test::GetMediaSessionInfoSync(&media_session))); } } TEST_F(AudioFocusManagerTest, AbandonAudioFocus_WhileDuckingThenResume) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); - ASSERT_TRUE(IsSessionDucking(media_session_1)); + AbandonAudioFocus(request_id_1); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); - AbandonAudioFocus(media_session_1); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); + AbandonAudioFocus(request_id_2); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); - AbandonAudioFocus(media_session_2); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); - - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); } TEST_F(AudioFocusManagerTest, AbandonAudioFocus_StopsDucking) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - AbandonAudioFocus(media_session_2); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AbandonAudioFocus(request_id_2); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); } TEST_F(AudioFocusManagerTest, DuckWhilePlaying) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_FALSE(IsSessionDucking(media_session_1)); - - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_TRUE(IsSessionDucking(media_session_1)); + RequestAudioFocus(&media_session_2, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); } TEST_F(AudioFocusManagerTest, GainSuspendsTransient) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + RequestAudioFocus(&media_session_2, AudioFocusType::kGainTransientMayDuck); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_TRUE(media_session_2->IsSuspended()); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_EQ(MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); } TEST_F(AudioFocusManagerTest, DuckWithMultipleTransients) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + MockMediaSession media_session_1; + MockMediaSession media_session_2; + MockMediaSession media_session_3; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - std::unique_ptr<WebContents> web_contents_3(CreateWebContents()); - MediaSessionImpl* media_session_3 = - MediaSessionImpl::Get(web_contents_3.get()); + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AudioFocusManager::RequestId request_id_3 = RequestAudioFocus( + &media_session_3, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_TRUE(IsSessionDucking(media_session_1)); + AbandonAudioFocus(request_id_2); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - RequestAudioFocus(media_session_3, AudioFocusType::kGainTransientMayDuck); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - AbandonAudioFocus(media_session_2); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - AbandonAudioFocus(media_session_3); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AbandonAudioFocus(request_id_3); + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); } -TEST_F(AudioFocusManagerTest, WebContentsDestroyed_ReleasesFocus) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); +TEST_F(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesFocus) { + { + MockMediaSession media_session; - RequestAudioFocus(media_session, AudioFocusType::kGain); - ASSERT_EQ(media_session, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + } - web_contents.reset(); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); + // If the media session is destroyed without abandoning audio focus we do not + // know until we next interact with the manager. + MockMediaSession media_session; + RequestAudioFocus(&media_session, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); } -TEST_F(AudioFocusManagerTest, WebContentsDestroyed_ReleasesTransients) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); +TEST_F(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesTransients) { + { + MockMediaSession media_session; + RequestAudioFocus(&media_session, AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + } - RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); - ASSERT_EQ(1, GetTransientMaybeDuckCount()); - - web_contents.reset(); - ASSERT_EQ(0, GetTransientMaybeDuckCount()); + // If the media session is destroyed without abandoning audio focus we do not + // know until we next interact with the manager. + MockMediaSession media_session; + RequestAudioFocus(&media_session, AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); } -TEST_F(AudioFocusManagerTest, WebContentsDestroyed_StopsDucking) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); +TEST_F(AudioFocusManagerTest, GainDucksForceDuck) { + MockMediaSession media_session_1(true /* force_duck */); + MockMediaSession media_session_2; - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); - RequestAudioFocus(media_session_1, AudioFocusType::kGain); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + AudioFocusManager::RequestId request_id_2 = + RequestAudioFocus(&media_session_2, AudioFocusType::kGain); - RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - web_contents_2.reset(); - ASSERT_FALSE(IsSessionDucking(media_session_1)); + EXPECT_EQ(request_id_2, GetAudioFocusedSession()); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); } -TEST_F(AudioFocusManagerTest, PepperRequestsGainFocus) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); +TEST_F(AudioFocusManagerTest, + AbandoningGainFocusRevokesTopMostForceDuckSession) { + MockMediaSession media_session_1(true /* force_duck */); + MockMediaSession media_session_2; + MockMediaSession media_session_3; - media_session->AddPlayer( - pepper_observer_.get(), 0, media::MediaContentType::Pepper); - ASSERT_EQ(media_session, GetAudioFocusedSession()); + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, AudioFocusType::kGain); + RequestAudioFocus(&media_session_2, AudioFocusType::kGain); - media_session->RemovePlayer(pepper_observer_.get(), 0); - ASSERT_EQ(nullptr, GetAudioFocusedSession()); -} + AudioFocusManager::RequestId request_id_3 = + RequestAudioFocus(&media_session_3, AudioFocusType::kGain); + EXPECT_EQ(request_id_3, GetAudioFocusedSession()); -TEST_F(AudioFocusManagerTest, GainDucksPepper) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); + EXPECT_EQ(MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); - - media_session_1->AddPlayer( - pepper_observer_.get(), 0, media::MediaContentType::Pepper); - - RequestAudioFocus(media_session_2, AudioFocusType::kGain); - - ASSERT_EQ(media_session_2, GetAudioFocusedSession()); - ASSERT_TRUE(media_session_1->IsActive()); - ASSERT_TRUE(IsSessionDucking(media_session_1)); -} - -TEST_F(AudioFocusManagerTest, AbandoningGainFocusRevokesTopMostPepperSession) { - std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); - MediaSessionImpl* media_session_1 = - MediaSessionImpl::Get(web_contents_1.get()); - - std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); - MediaSessionImpl* media_session_2 = - MediaSessionImpl::Get(web_contents_2.get()); - - std::unique_ptr<WebContents> web_contents_3(CreateWebContents()); - MediaSessionImpl* media_session_3 = - MediaSessionImpl::Get(web_contents_3.get()); - - media_session_1->AddPlayer( - pepper_observer_.get(), 0, media::MediaContentType::Pepper); - - RequestAudioFocus(media_session_2, AudioFocusType::kGain); - RequestAudioFocus(media_session_3, AudioFocusType::kGain); - - ASSERT_EQ(media_session_3, GetAudioFocusedSession()); - ASSERT_TRUE(media_session_2->IsSuspended()); - ASSERT_TRUE(media_session_1->IsActive()); - ASSERT_TRUE(IsSessionDucking(media_session_1)); - - AbandonAudioFocus(media_session_3); - ASSERT_EQ(media_session_1, GetAudioFocusedSession()); + AbandonAudioFocus(request_id_3); + EXPECT_EQ(request_id_1, GetAudioFocusedSession()); } TEST_F(AudioFocusManagerTest, AudioFocusObserver_AbandonNoop) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + test::TestAudioFocusObserver observer; + AbandonAudioFocus(kNoFocusedSession); - { - MockAudioFocusObserver observer; - AbandonAudioFocus(media_session); - FlushForTesting(); - - EXPECT_EQ(nullptr, GetAudioFocusedSession()); - EXPECT_FALSE(observer.focus_lost_call_); - } + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_TRUE(observer.focus_lost_session_.is_null()); } TEST_F(AudioFocusManagerTest, AudioFocusObserver_RequestNoop) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; + AudioFocusManager::RequestId request_id; { - MockAudioFocusObserver observer; - RequestAudioFocus(media_session, AudioFocusType::kGain); - FlushForTesting(); + test::TestAudioFocusObserver observer; + request_id = RequestAudioFocus(&media_session, AudioFocusType::kGain); - EXPECT_EQ(media_session, GetAudioFocusedSession()); - EXPECT_EQ(AudioFocusType::kGain, observer.focus_gained_call_.value()); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(AudioFocusType::kGain, observer.focus_gained_type()); + EXPECT_FALSE(observer.focus_gained_session_.is_null()); } { - MockAudioFocusObserver observer; - RequestAudioFocus(media_session, AudioFocusType::kGain); - FlushForTesting(); + test::TestAudioFocusObserver observer; + RequestAudioFocus(&media_session, AudioFocusType::kGain, request_id); - EXPECT_EQ(media_session, GetAudioFocusedSession()); - EXPECT_FALSE(observer.focus_gained_call_.has_value()); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_TRUE(observer.focus_gained_session_.is_null()); } } TEST_F(AudioFocusManagerTest, AudioFocusObserver_TransientMayDuck) { - std::unique_ptr<WebContents> web_contents(CreateWebContents()); - MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + MockMediaSession media_session; + AudioFocusManager::RequestId request_id; { - MockAudioFocusObserver observer; - RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); - FlushForTesting(); + test::TestAudioFocusObserver observer; + request_id = RequestAudioFocus(&media_session, + AudioFocusType::kGainTransientMayDuck); EXPECT_EQ(1, GetTransientMaybeDuckCount()); EXPECT_EQ(AudioFocusType::kGainTransientMayDuck, - observer.focus_gained_call_.value()); + observer.focus_gained_type()); + EXPECT_FALSE(observer.focus_gained_session_.is_null()); } { - MockAudioFocusObserver observer; - AbandonAudioFocus(media_session); - FlushForTesting(); + test::TestAudioFocusObserver observer; + AbandonAudioFocus(request_id); EXPECT_EQ(0, GetTransientMaybeDuckCount()); - EXPECT_TRUE(observer.focus_lost_call_); + EXPECT_TRUE(observer.focus_lost_session_.Equals( + test::GetMediaSessionInfoSync(&media_session))); } }
diff --git a/content/browser/media/session/audio_focus_observer.h b/content/browser/media/session/audio_focus_observer.h index c4eae2b..97ee55a 100644 --- a/content/browser/media/session/audio_focus_observer.h +++ b/content/browser/media/session/audio_focus_observer.h
@@ -23,11 +23,11 @@ ~AudioFocusObserver() override; // The given media session gained audio focus with the specified type. - void OnFocusGained(::media_session::mojom::MediaSessionPtr, + void OnFocusGained(::media_session::mojom::MediaSessionInfoPtr, media_session::mojom::AudioFocusType) override {} // The given media session lost audio focus. - void OnFocusLost(::media_session::mojom::MediaSessionPtr) override {} + void OnFocusLost(::media_session::mojom::MediaSessionInfoPtr) override {} protected: // Called by subclasses to (un-)register the observer with AudioFocusManager.
diff --git a/content/browser/media/session/audio_focus_test_util.cc b/content/browser/media/session/audio_focus_test_util.cc new file mode 100644 index 0000000..b25d0ee5 --- /dev/null +++ b/content/browser/media/session/audio_focus_test_util.cc
@@ -0,0 +1,73 @@ +// 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 "content/browser/media/session/audio_focus_test_util.h" + +namespace content { +namespace test { + +namespace { + +void ReceivedSessionInfo(media_session::mojom::MediaSessionInfoPtr* info_out, + base::RepeatingClosure callback, + media_session::mojom::MediaSessionInfoPtr result) { + *info_out = std::move(result); + std::move(callback).Run(); +} + +} // namespace + +TestAudioFocusObserver::TestAudioFocusObserver() { + RegisterAudioFocusObserver(); +} + +TestAudioFocusObserver::~TestAudioFocusObserver() = default; + +void TestAudioFocusObserver::OnFocusGained( + media_session::mojom::MediaSessionInfoPtr session, + media_session::mojom::AudioFocusType type) { + focus_gained_type_ = type; + focus_gained_session_ = std::move(session); + + if (wait_for_gained_) + run_loop_.Quit(); +} + +void TestAudioFocusObserver::OnFocusLost( + media_session::mojom::MediaSessionInfoPtr session) { + focus_lost_session_ = std::move(session); + + if (wait_for_lost_) + run_loop_.Quit(); +} + +void TestAudioFocusObserver::WaitForGainedEvent() { + if (!focus_gained_session_.is_null()) + return; + + wait_for_gained_ = true; + run_loop_.Run(); +} + +void TestAudioFocusObserver::WaitForLostEvent() { + if (!focus_lost_session_.is_null()) + return; + + wait_for_lost_ = true; + run_loop_.Run(); +} + +media_session::mojom::MediaSessionInfoPtr GetMediaSessionInfoSync( + media_session::mojom::MediaSession* session) { + media_session::mojom::MediaSessionInfoPtr session_info; + base::RunLoop run_loop; + + session->GetMediaSessionInfo(base::BindOnce( + &ReceivedSessionInfo, &session_info, run_loop.QuitClosure())); + + return session_info; +} + +} // namespace test +} // namespace content
diff --git a/content/browser/media/session/audio_focus_test_util.h b/content/browser/media/session/audio_focus_test_util.h new file mode 100644 index 0000000..3ed51660 --- /dev/null +++ b/content/browser/media/session/audio_focus_test_util.h
@@ -0,0 +1,54 @@ +// 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 CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_TEST_UTIL_H_ +#define CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_TEST_UTIL_H_ + +#include "base/run_loop.h" +#include "content/browser/media/session/audio_focus_observer.h" +#include "services/media_session/public/mojom/audio_focus.mojom.h" + +namespace content { +namespace test { + +class TestAudioFocusObserver : public AudioFocusObserver { + public: + TestAudioFocusObserver(); + ~TestAudioFocusObserver() override; + + void OnFocusGained(media_session::mojom::MediaSessionInfoPtr, + media_session::mojom::AudioFocusType) override; + + void OnFocusLost(media_session::mojom::MediaSessionInfoPtr) override; + + void WaitForGainedEvent(); + void WaitForLostEvent(); + + media_session::mojom::AudioFocusType focus_gained_type() const { + DCHECK(!focus_gained_session_.is_null()); + return focus_gained_type_; + } + + // These store the values we received. + media_session::mojom::MediaSessionInfoPtr focus_gained_session_; + media_session::mojom::MediaSessionInfoPtr focus_lost_session_; + + private: + media_session::mojom::AudioFocusType focus_gained_type_; + + // If either of these are true we will quit the run loop if we observe a gain + // or lost event. + bool wait_for_gained_ = false; + bool wait_for_lost_ = false; + + base::RunLoop run_loop_; +}; + +media_session::mojom::MediaSessionInfoPtr GetMediaSessionInfoSync( + media_session::mojom::MediaSession*); + +} // namespace test +} // namespace content + +#endif // CONTENT_BROWSER_MEDIA_SESSION_AUDIO_FOCUS_TEST_UTIL_H_
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc index 2f712fd0..47b45ec 100644 --- a/content/browser/media/session/media_session_impl.cc +++ b/content/browser/media/session/media_session_impl.cc
@@ -29,6 +29,7 @@ namespace content { using MediaSessionUserAction = MediaSessionUmaHelper::MediaSessionUserAction; +using media_session::mojom::MediaSessionInfo; namespace { @@ -43,6 +44,11 @@ using MapRenderFrameHostToDepth = std::map<RenderFrameHost*, size_t>; +using media_session::mojom::AudioFocusType; + +using MediaSessionSuspendedSource = + MediaSessionUmaHelper::MediaSessionSuspendedSource; + size_t ComputeFrameDepth(RenderFrameHost* rfh, MapRenderFrameHostToDepth* map_rfh_to_depth) { DCHECK(rfh); @@ -90,11 +96,6 @@ } // anonymous namespace -using media_session::mojom::AudioFocusType; - -using MediaSessionSuspendedSource = - MediaSessionUmaHelper::MediaSessionSuspendedSource; - MediaSessionImpl::PlayerIdentifier::PlayerIdentifier( MediaSessionPlayerObserver* observer, int player_id) @@ -105,6 +106,12 @@ return this->observer == other.observer && this->player_id == other.player_id; } +bool MediaSessionImpl::PlayerIdentifier::operator<( + const PlayerIdentifier& other) const { + return MediaSessionImpl::PlayerIdentifier::Hash()(*this) < + MediaSessionImpl::PlayerIdentifier::Hash()(other); +} + size_t MediaSessionImpl::PlayerIdentifier::Hash::operator()( const PlayerIdentifier& player_identifier) const { size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionPlayerObserver*>()( @@ -151,6 +158,7 @@ normal_players_.clear(); pepper_players_.clear(); one_shot_players_.clear(); + AbandonSystemAudioFocusIfNeeded(); } @@ -217,19 +225,27 @@ else required_audio_focus_type = AudioFocusType::kGainTransientMayDuck; + PlayerIdentifier key(observer, player_id); + // If the audio focus is already granted and is of type Content, there is // nothing to do. If it is granted of type Transient the requested type is // also transient, there is also nothing to do. Otherwise, the session needs // to request audio focus again. if (audio_focus_state_ == State::ACTIVE) { - AudioFocusType current_focus_type = delegate_->GetCurrentFocusType(); + base::Optional<AudioFocusType> current_focus_type = + delegate_->GetCurrentFocusType(); if (current_focus_type == AudioFocusType::kGain || current_focus_type == required_audio_focus_type) { - normal_players_.insert(PlayerIdentifier(observer, player_id)); + auto iter = normal_players_.find(key); + if (iter == normal_players_.end()) + normal_players_.emplace(std::move(key), required_audio_focus_type); + else + iter->second = required_audio_focus_type; return true; } } + bool old_controllable = IsControllable(); State old_audio_focus_state = audio_focus_state_; RequestSystemAudioFocus(required_audio_focus_type); @@ -241,10 +257,19 @@ if (old_audio_focus_state != State::ACTIVE) normal_players_.clear(); - normal_players_.insert(PlayerIdentifier(observer, player_id)); + auto iter = normal_players_.find(key); + if (iter == normal_players_.end()) + normal_players_.emplace(std::move(key), required_audio_focus_type); + else + iter->second = required_audio_focus_type; UpdateRoutedService(); - NotifyAboutStateChange(); + + if (old_audio_focus_state != audio_focus_state_ || + old_controllable != IsControllable()) { + NotifyAboutStateChange(); + } + return true; } @@ -254,11 +279,11 @@ PlayerIdentifier identifier(observer, player_id); - auto it = normal_players_.find(identifier); - if (it != normal_players_.end()) - normal_players_.erase(it); + auto iter = normal_players_.find(identifier); + if (iter != normal_players_.end()) + normal_players_.erase(iter); - it = pepper_players_.find(identifier); + auto it = pepper_players_.find(identifier); if (it != pepper_players_.end()) pepper_players_.erase(it); @@ -280,7 +305,7 @@ bool was_controllable = IsControllable(); for (auto it = normal_players_.begin(); it != normal_players_.end();) { - if (it->observer == observer) + if (it->first.observer == observer) normal_players_.erase(it++); else ++it; @@ -361,11 +386,14 @@ // must be requested. if (suspend_type != SuspendType::kSystem) { // Request audio focus again in case we lost it because another app started - // playing while the playback was paused. - State audio_focus_state = RequestSystemAudioFocus(desired_audio_focus_type_) - ? State::ACTIVE - : State::INACTIVE; - SetAudioFocusState(audio_focus_state); + // playing while the playback was paused. If the audio focus request is + // delayed we will resume the player when the request completes. + AudioFocusDelegate::AudioFocusResult result = + RequestSystemAudioFocus(desired_audio_focus_type_); + + SetAudioFocusState(result != AudioFocusDelegate::AudioFocusResult::kFailed + ? State::ACTIVE + : State::INACTIVE); if (audio_focus_state_ != State::ACTIVE) return; @@ -413,12 +441,12 @@ void MediaSessionImpl::SeekForward(base::TimeDelta seek_time) { for (const auto& it : normal_players_) - it.observer->OnSeekForward(it.player_id, seek_time); + it.first.observer->OnSeekForward(it.first.player_id, seek_time); } void MediaSessionImpl::SeekBackward(base::TimeDelta seek_time) { for (const auto& it : normal_players_) - it.observer->OnSeekBackward(it.player_id, seek_time); + it.first.observer->OnSeekBackward(it.first.player_id, seek_time); } bool MediaSessionImpl::IsControllable() const { @@ -448,6 +476,7 @@ return; is_ducking_ = true; UpdateVolumeMultiplier(); + NotifyObserversInfoChanged(); } void MediaSessionImpl::StopDucking() { @@ -455,11 +484,15 @@ return; is_ducking_ = false; UpdateVolumeMultiplier(); + NotifyObserversInfoChanged(); } void MediaSessionImpl::UpdateVolumeMultiplier() { - for (const auto& it : normal_players_) - it.observer->OnSetVolumeMultiplier(it.player_id, GetVolumeMultiplier()); + for (const auto& it : normal_players_) { + it.first.observer->OnSetVolumeMultiplier(it.first.player_id, + GetVolumeMultiplier()); + } + for (const auto& it : pepper_players_) it.observer->OnSetVolumeMultiplier(it.player_id, GetVolumeMultiplier()); } @@ -502,6 +535,12 @@ AbandonSystemAudioFocusIfNeeded(); } +void MediaSessionImpl::OnSystemAudioFocusRequested(bool result) { + uma_helper_.RecordRequestAudioFocusResult(result); + if (result) + StopDucking(); +} + void MediaSessionImpl::OnSuspendInternal(SuspendType suspend_type, State new_state) { DCHECK(!HasPepper()); @@ -548,7 +587,7 @@ // the page in which case the player is already paused. // Otherwise, the players need to be paused. for (const auto& it : normal_players_) - it.observer->OnSuspend(it.player_id); + it.first.observer->OnSuspend(it.first.player_id); } for (const auto& it : pepper_players_) @@ -565,7 +604,7 @@ SetAudioFocusState(State::ACTIVE); for (const auto& it : normal_players_) - it.observer->OnResume(it.player_id); + it.first.observer->OnResume(it.first.player_id); for (const auto& it : pepper_players_) it.observer->OnSetVolumeMultiplier(it.player_id, GetVolumeMultiplier()); @@ -589,29 +628,37 @@ delegate_ = AudioFocusDelegate::Create(this); } -bool MediaSessionImpl::RequestSystemAudioFocus( +AudioFocusDelegate::AudioFocusResult MediaSessionImpl::RequestSystemAudioFocus( AudioFocusType audio_focus_type) { - bool result = delegate_->RequestAudioFocus(audio_focus_type); - uma_helper_.RecordRequestAudioFocusResult(result); - - // Make sure we are unducked. - if (result) - StopDucking(); - - // MediaSessionImpl must change its state & audio focus type AFTER requesting - // audio focus. - SetAudioFocusState(result ? State::ACTIVE : State::INACTIVE); + AudioFocusDelegate::AudioFocusResult result = + delegate_->RequestAudioFocus(audio_focus_type); desired_audio_focus_type_ = audio_focus_type; + + bool success = result != AudioFocusDelegate::AudioFocusResult::kFailed; + SetAudioFocusState(success ? State::ACTIVE : State::INACTIVE); + + // If we are delayed then we should return now and wait for the response from + // the audio focus delegate. + if (result == AudioFocusDelegate::AudioFocusResult::kDelayed) + return result; + + OnSystemAudioFocusRequested(success); return result; } -const MediaSessionImpl::DebugInfo MediaSessionImpl::GetDebugInfo() { - MediaSessionImpl::DebugInfo debug_info; +void MediaSessionImpl::BindToMojoRequest( + mojo::InterfaceRequest<media_session::mojom::MediaSession> request) { + bindings_.AddBinding(this, std::move(request)); +} + +void MediaSessionImpl::GetDebugInfo(GetDebugInfoCallback callback) { + media_session::mojom::MediaSessionDebugInfoPtr info( + media_session::mojom::MediaSessionDebugInfo::New()); // Convert the address of |this| to a string and use it as the name. std::stringstream stream; stream << this; - debug_info.name = stream.str(); + info->name = stream.str(); // Add the title and the url to the owner. std::vector<std::string> owner_parts; @@ -619,21 +666,84 @@ base::UTF16ToUTF8(web_contents()->GetTitle())); MaybePushBackString(owner_parts, web_contents()->GetLastCommittedURL().spec()); - debug_info.owner = base::JoinString(owner_parts, kDebugInfoOwnerSeparator); + info->owner = base::JoinString(owner_parts, kDebugInfoOwnerSeparator); // Add the ducking state to state. std::vector<std::string> state_parts; MaybePushBackString(state_parts, IsActive() ? kDebugInfoActive : kDebugInfoInactive); MaybePushBackString(state_parts, is_ducking_ ? kDebugInfoDucked : ""); - debug_info.state = base::JoinString(state_parts, kDebugInfoStateSeparator); + info->state = base::JoinString(state_parts, kDebugInfoStateSeparator); - return debug_info; + std::move(callback).Run(std::move(info)); } -void MediaSessionImpl::BindToMojoRequest( - mojo::InterfaceRequest<media_session::mojom::MediaSession> request) { - bindings_.AddBinding(this, std::move(request)); +media_session::mojom::MediaSessionInfoPtr +MediaSessionImpl::GetMediaSessionInfoSync() { + media_session::mojom::MediaSessionInfoPtr info( + media_session::mojom::MediaSessionInfo::New()); + + switch (audio_focus_state_) { + case State::ACTIVE: + info->state = MediaSessionInfo::SessionState::kActive; + break; + case State::SUSPENDED: + info->state = MediaSessionInfo::SessionState::kSuspended; + break; + case State::INACTIVE: + info->state = MediaSessionInfo::SessionState::kInactive; + break; + } + + // The state should always be kDucked if we are ducked. + if (is_ducking_) + info->state = MediaSessionInfo::SessionState::kDucking; + + // If we have Pepper players then we should force ducking. + info->force_duck = HasPepper(); + return info; +} + +void MediaSessionImpl::GetMediaSessionInfo( + GetMediaSessionInfoCallback callback) { + std::move(callback).Run(GetMediaSessionInfoSync()); +} + +void MediaSessionImpl::AddObserver( + media_session::mojom::MediaSessionObserverPtr observer) { + observer->MediaSessionInfoChanged(GetMediaSessionInfoSync()); + mojo_observers_.AddPtr(std::move(observer)); +} + +void MediaSessionImpl::FinishSystemAudioFocusRequest( + AudioFocusType audio_focus_type, + bool result) { + // If the media session is not active then we do not need to enforce the + // result of the audio focus request. + if (audio_focus_state_ != State::ACTIVE) { + AbandonSystemAudioFocusIfNeeded(); + return; + } + + OnSystemAudioFocusRequested(result); + + if (!result) { + switch (audio_focus_type) { + case AudioFocusType::kGain: + // If the gain audio focus request failed then we should suspend the + // media session. + OnSuspendInternal(SuspendType::kSystem, State::SUSPENDED); + break; + case AudioFocusType::kGainTransientMayDuck: + // The focus request failed, we should suspend any players that have + // the same audio focus type. + for (auto& player : normal_players_) { + if (audio_focus_type == player.second) + player.first.observer->OnSuspend(player.first.player_id); + } + break; + } + } } void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() { @@ -672,24 +782,44 @@ uma_helper_.OnSessionInactive(); break; } + + NotifyObserversInfoChanged(); +} + +void MediaSessionImpl::FlushForTesting() { + mojo_observers_.FlushForTesting(); +} + +void MediaSessionImpl::NotifyObserversInfoChanged() { + media_session::mojom::MediaSessionInfoPtr current_info = + GetMediaSessionInfoSync(); + + mojo_observers_.ForAllPtrs( + [¤t_info](media_session::mojom::MediaSessionObserver* observer) { + observer->MediaSessionInfoChanged(current_info.Clone()); + }); } bool MediaSessionImpl::AddPepperPlayer(MediaSessionPlayerObserver* observer, int player_id) { - bool success = RequestSystemAudioFocus(AudioFocusType::kGain); - DCHECK(success); + AudioFocusDelegate::AudioFocusResult result = + RequestSystemAudioFocus(AudioFocusType::kGain); + DCHECK_NE(AudioFocusDelegate::AudioFocusResult::kFailed, result); pepper_players_.insert(PlayerIdentifier(observer, player_id)); observer->OnSetVolumeMultiplier(player_id, GetVolumeMultiplier()); NotifyAboutStateChange(); - return success; + return result != AudioFocusDelegate::AudioFocusResult::kFailed; } bool MediaSessionImpl::AddOneShotPlayer(MediaSessionPlayerObserver* observer, int player_id) { - if (!RequestSystemAudioFocus(AudioFocusType::kGain)) + AudioFocusDelegate::AudioFocusResult result = + RequestSystemAudioFocus(AudioFocusType::kGain); + + if (result == AudioFocusDelegate::AudioFocusResult::kFailed) return false; one_shot_players_.insert(PlayerIdentifier(observer, player_id)); @@ -762,8 +892,8 @@ RenderFrameHost* rfh_of_routed_service = routed_service_ ? routed_service_->GetRenderFrameHost() : nullptr; for (const auto& player : normal_players_) { - if (player.observer->render_frame_host() != rfh_of_routed_service) - player.observer->OnSuspend(player.player_id); + if (player.first.observer->render_frame_host() != rfh_of_routed_service) + player.first.observer->OnSuspend(player.first.player_id); } for (const auto& player : pepper_players_) { if (player.observer->render_frame_host() != rfh_of_routed_service) { @@ -806,7 +936,7 @@ // prefer the top-most frame. std::set<RenderFrameHost*> frames; for (const auto& player : normal_players_) { - RenderFrameHost* frame = player.observer->render_frame_host(); + RenderFrameHost* frame = player.first.observer->render_frame_host(); if (frame) frames.insert(frame); }
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h index 42f85c7..daf67db 100644 --- a/content/browser/media/session/media_session_impl.h +++ b/content/browser/media/session/media_session_impl.h
@@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "base/optional.h" +#include "content/browser/media/session/audio_focus_delegate.h" #include "content/browser/media/session/audio_focus_manager.h" #include "content/browser/media/session/media_session_uma_helper.h" #include "content/common/content_export.h" @@ -25,6 +26,7 @@ #include "content/public/common/media_metadata.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" #include "services/media_session/public/mojom/audio_focus.mojom.h" #if defined(OS_ANDROID) @@ -37,15 +39,8 @@ enum class MediaContentType; } // namespace media -namespace media_session { -namespace mojom { -enum class AudioFocusType; -} // namespace mojom -} // namespace media_session - namespace content { -class AudioFocusDelegate; class AudioFocusManagerTest; class MediaSessionImplServiceRoutingTest; class MediaSessionImplStateObserver; @@ -124,43 +119,6 @@ CONTENT_EXPORT void OnPlayerPaused(MediaSessionPlayerObserver* observer, int player_id); - // Resume the media session. - // |type| represents the origin of the request. - CONTENT_EXPORT void Resume(MediaSession::SuspendType suspend_type) override; - - // Suspend the media session. - // |type| represents the origin of the request. - CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override; - - // Stop the media session. - // |type| represents the origin of the request. - CONTENT_EXPORT void Stop(MediaSession::SuspendType suspend_type) override; - - // Seek the media session forward. - CONTENT_EXPORT void SeekForward(base::TimeDelta seek_time) override; - - // Seek the media session backward. - CONTENT_EXPORT void SeekBackward(base::TimeDelta seek_time) override; - - // Returns if the session can be controlled by Resume() and Suspend() calls - // above. - CONTENT_EXPORT bool IsControllable() const override; - - // Compute if the actual playback state is paused by combining the - // MediaSessionService declared state and guessed state (audio_focus_state_). - CONTENT_EXPORT bool IsActuallyPaused() const override; - - // Set the volume multiplier applied during ducking. - CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override; - - // Let the media session start ducking such that the volume multiplier is - // reduced. - CONTENT_EXPORT void StartDucking() override; - - // Let the media session stop ducking such that the volume multiplier is - // recovered. - CONTENT_EXPORT void StopDucking() override; - void AddObserver(MediaSessionObserver* observer) override; void RemoveObserver(MediaSessionObserver* observer) override; @@ -198,32 +156,78 @@ // observers if the service is currently routed. void OnMediaSessionActionsChanged(MediaSessionServiceImpl* service); - // Called when a MediaSessionAction is received. The action will be forwarded - // to blink::MediaSession corresponding to the current routed service. - void DidReceiveAction(blink::mojom::MediaSessionAction action) override; - // Requests audio focus to the AudioFocusDelegate. // Returns whether the request was granted. - CONTENT_EXPORT bool RequestSystemAudioFocus( + CONTENT_EXPORT AudioFocusDelegate::AudioFocusResult RequestSystemAudioFocus( media_session::mojom::AudioFocusType audio_focus_type); - // Returns debugging information to be displayed on chrome://media-internals. - struct DebugInfo { - // A unique name for the MediaSession. - std::string name; - - // The title and URL of the owning WebContents. - std::string owner; - - // State information stored in a string e.g. Ducked. - std::string state; - }; - const DebugInfo GetDebugInfo(); - // Creates a binding between |this| and |request|. void BindToMojoRequest( mojo::InterfaceRequest<media_session::mojom::MediaSession> request); + // Returns information about the MediaSession. + media_session::mojom::MediaSessionInfoPtr GetMediaSessionInfoSync(); + + // MediaSession overrides --------------------------------------------------- + + // Resume the media session. + // |type| represents the origin of the request. + CONTENT_EXPORT void Resume(MediaSession::SuspendType suspend_type) override; + + // Stop the media session. + // |type| represents the origin of the request. + CONTENT_EXPORT void Stop(MediaSession::SuspendType suspend_type) override; + + // Seek the media session forward. + CONTENT_EXPORT void SeekForward(base::TimeDelta seek_time) override; + + // Seek the media session backward. + CONTENT_EXPORT void SeekBackward(base::TimeDelta seek_time) override; + + // Returns if the session can be controlled by Resume() and Suspend() calls + // above. + CONTENT_EXPORT bool IsControllable() const override; + + // Compute if the actual playback state is paused by combining the + // MediaSessionService declared state and guessed state (audio_focus_state_). + CONTENT_EXPORT bool IsActuallyPaused() const override; + + // Called when a MediaSessionAction is received. The action will be forwarded + // to blink::MediaSession corresponding to the current routed service. + void DidReceiveAction(blink::mojom::MediaSessionAction action) override; + + // Set the volume multiplier applied during ducking. + CONTENT_EXPORT void SetDuckingVolumeMultiplier(double multiplier) override; + + // Suspend the media session. + // |type| represents the origin of the request. + CONTENT_EXPORT void Suspend(MediaSession::SuspendType suspend_type) override; + + // Let the media session start ducking such that the volume multiplier is + // reduced. + CONTENT_EXPORT void StartDucking() override; + + // Let the media session stop ducking such that the volume multiplier is + // recovered. + CONTENT_EXPORT void StopDucking() override; + + // Returns information about the MediaSession. The sync method is not actually + // slower and should be used over the async one which is available over mojo. + void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override; + + // Returns debugging information to be displayed on chrome://media-internals. + void GetDebugInfo(GetDebugInfoCallback) override; + + // Adds a mojo based observer to listen to events related to this session. + void AddObserver( + media_session::mojom::MediaSessionObserverPtr observer) override; + + // Called by |AudioFocusDelegate| when an async audio focus request is + // completed. + CONTENT_EXPORT void FinishSystemAudioFocusRequest( + media_session::mojom::AudioFocusType type, + bool result); + private: friend class content::WebContentsUserData<MediaSessionImpl>; friend class ::MediaSessionImplBrowserTest; @@ -232,6 +236,7 @@ friend class content::MediaSessionImplServiceRoutingTest; friend class content::MediaSessionImplStateObserver; friend class content::MediaSessionServiceImplBrowserTest; + friend class MediaSessionImplTest; friend class MediaInternalsAudioFocusTest; CONTENT_EXPORT void SetDelegateForTests( @@ -246,6 +251,7 @@ void operator=(const PlayerIdentifier&) = delete; bool operator==(const PlayerIdentifier& player_identifier) const; + bool operator<(const PlayerIdentifier&) const; // Hash operator for base::hash_map<>. struct Hash { @@ -262,6 +268,10 @@ void Initialize(); + // Called when system audio focus has been requested and whether the request + // was granted. + void OnSystemAudioFocusRequested(bool result); + CONTENT_EXPORT void OnSuspendInternal(MediaSession::SuspendType suspend_type, State new_state); CONTENT_EXPORT void OnResumeInternal(MediaSession::SuspendType suspend_type); @@ -280,6 +290,12 @@ // It sets audio_focus_state_ and notifies observers about the state change. void SetAudioFocusState(State audio_focus_state); + // Flushes any mojo bindings for testing. + CONTENT_EXPORT void FlushForTesting(); + + // Notifies mojo observers that the MediaSessionInfo has changed. + void NotifyObserversInfoChanged(); + // Update the volume multiplier when ducking state changes. void UpdateVolumeMultiplier(); @@ -311,7 +327,8 @@ CONTENT_EXPORT MediaSessionServiceImpl* ComputeServiceForRouting(); std::unique_ptr<AudioFocusDelegate> delegate_; - PlayersMap normal_players_; + std::map<PlayerIdentifier, media_session::mojom::AudioFocusType> + normal_players_; PlayersMap pepper_players_; PlayersMap one_shot_players_; @@ -352,6 +369,9 @@ // Bindings for Mojo pointers to |this| held by media route providers. mojo::BindingSet<media_session::mojom::MediaSession> bindings_; + mojo::InterfacePtrSet<media_session::mojom::MediaSessionObserver> + mojo_observers_; + DISALLOW_COPY_AND_ASSIGN(MediaSessionImpl); };
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc index f7ba08d..ca9dfdc9 100644 --- a/content/browser/media/session/media_session_impl_browsertest.cc +++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -51,21 +51,44 @@ class MockAudioFocusDelegate : public AudioFocusDelegate { public: - MockAudioFocusDelegate() {} + MockAudioFocusDelegate(MediaSessionImpl* media_session, bool async_mode) + : media_session_(media_session), async_mode_(async_mode) {} MOCK_METHOD0(AbandonAudioFocus, void()); - bool RequestAudioFocus(AudioFocusType audio_focus_type) { - audio_focus_type_ = audio_focus_type; - return true; + AudioFocusDelegate::AudioFocusResult RequestAudioFocus( + AudioFocusType audio_focus_type) { + if (async_mode_) { + requests_.push_back(audio_focus_type); + return AudioFocusDelegate::AudioFocusResult::kDelayed; + } else { + audio_focus_type_ = audio_focus_type; + return AudioFocusDelegate::AudioFocusResult::kSuccess; + } } - AudioFocusType GetCurrentFocusType() const { - DCHECK(audio_focus_type_.has_value()); - return audio_focus_type_.value(); + base::Optional<AudioFocusType> GetCurrentFocusType() const { + return audio_focus_type_; } + void ResolveRequest(bool result) { + if (!async_mode_) + return; + + audio_focus_type_ = requests_.front(); + requests_.pop_front(); + + media_session_->FinishSystemAudioFocusRequest(audio_focus_type_.value(), + result); + } + + bool HasRequests() const { return !requests_.empty(); } + private: + MediaSessionImpl* media_session_; + const bool async_mode_ = false; + + std::list<AudioFocusType> requests_; base::Optional<AudioFocusType> audio_focus_type_; }; @@ -88,7 +111,8 @@ media_session_ = MediaSessionImpl::Get(shell()->web_contents()); mock_media_session_observer_.reset( new NiceMock<content::MockMediaSessionObserver>(media_session_)); - mock_audio_focus_delegate_ = new NiceMock<MockAudioFocusDelegate>; + mock_audio_focus_delegate_ = new NiceMock<MockAudioFocusDelegate>( + media_session_, true /* async_mode */); media_session_->SetDelegateForTests( base::WrapUnique(mock_audio_focus_delegate_)); ASSERT_TRUE(media_session_); @@ -106,8 +130,10 @@ void StartNewPlayer(MockMediaSessionPlayerObserver* player_observer, media::MediaContentType media_content_type) { - bool result = AddPlayer(player_observer, player_observer->StartNewPlayer(), - media_content_type); + int player_id = player_observer->StartNewPlayer(); + + bool result = AddPlayer(player_observer, player_id, media_content_type); + EXPECT_TRUE(result); } @@ -133,7 +159,7 @@ bool IsActive() { return media_session_->IsActive(); } - AudioFocusType GetSessionAudioFocusType() { + base::Optional<AudioFocusType> GetSessionAudioFocusType() { return mock_audio_focus_delegate_->GetCurrentFocusType(); } @@ -175,6 +201,18 @@ mock_media_session_service_->SetPlaybackState(state); } + void ResolveAudioFocusSuccess() { + mock_audio_focus_delegate()->ResolveRequest(true /* result */); + } + + void ResolveAudioFocusFailure() { + mock_audio_focus_delegate()->ResolveRequest(false /* result */); + } + + bool HasUnresolvedAudioFocusRequest() { + return mock_audio_focus_delegate()->HasRequests(); + } + content::MockMediaSessionObserver* mock_media_session_observer() { return mock_media_session_observer_.get(); } @@ -191,6 +229,14 @@ return media_session_->uma_helper_for_test(); } + void SetAudioFocusDelegateForTests(MockAudioFocusDelegate* delegate) { + mock_audio_focus_delegate_ = delegate; + media_session_->SetDelegateForTests( + base::WrapUnique(mock_audio_focus_delegate_)); + } + + bool IsDucking() const { return media_session_->is_ducking_; } + protected: MediaSessionImpl* media_session_; std::unique_ptr<content::MockMediaSessionObserver> @@ -201,20 +247,37 @@ DISALLOW_COPY_AND_ASSIGN(MediaSessionImplBrowserTest); }; -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +class MediaSessionImplParamBrowserTest + : public MediaSessionImplBrowserTest, + public testing::WithParamInterface<bool> { + protected: + MediaSessionImplParamBrowserTest() = default; + + void SetUpOnMainThread() override { + MediaSessionImplBrowserTest::SetUpOnMainThread(); + + SetAudioFocusDelegateForTests( + new NiceMock<MockAudioFocusDelegate>(media_session_, GetParam())); + } +}; + +INSTANTIATE_TEST_CASE_P(, MediaSessionImplParamBrowserTest, testing::Bool()); + +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, PlayersFromSameObserverDoNotStopEachOtherInSameSession) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(player_observer->IsPlaying(0)); EXPECT_TRUE(player_observer->IsPlaying(1)); EXPECT_TRUE(player_observer->IsPlaying(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, PlayersFromManyObserverDoNotStopEachOtherInSameSession) { auto player_observer_1 = std::make_unique<MockMediaSessionPlayerObserver>(); auto player_observer_2 = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -223,19 +286,21 @@ StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_3.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(player_observer_1->IsPlaying(0)); EXPECT_TRUE(player_observer_2->IsPlaying(0)); EXPECT_TRUE(player_observer_3->IsPlaying(0)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, SuspendedMediaSessionStopsPlayers) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); @@ -244,13 +309,14 @@ EXPECT_FALSE(player_observer->IsPlaying(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ResumedMediaSessionRestartsPlayers) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); SystemResume(); @@ -260,11 +326,12 @@ EXPECT_TRUE(player_observer->IsPlaying(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, StartedPlayerOnSuspendedSessionPlaysAlone) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(player_observer->IsPlaying(0)); @@ -273,6 +340,7 @@ EXPECT_FALSE(player_observer->IsPlaying(0)); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_FALSE(player_observer->IsPlaying(0)); EXPECT_TRUE(player_observer->IsPlaying(1)); @@ -284,7 +352,8 @@ EXPECT_TRUE(player_observer->IsPlaying(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, InitialVolumeMultiplier) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + InitialVolumeMultiplier) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); @@ -292,9 +361,14 @@ EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(0)); EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(1)); + + ResolveAudioFocusSuccess(); + + EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(0)); + EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(1)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, StartDuckingReducesVolumeMultiplier) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -310,7 +384,7 @@ EXPECT_EQ(kDuckingVolumeMultiplier, player_observer->GetVolumeMultiplier(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, StopDuckingRecoversVolumeMultiplier) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -327,7 +401,7 @@ EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(2)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, DuckingUsesConfiguredMultiplier) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -344,14 +418,16 @@ EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(1)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, AudioFocusInitialState) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + AudioFocusInitialState) { EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, AddPlayerOnSuspendedFocusUnducks) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); EXPECT_FALSE(IsActive()); @@ -361,53 +437,65 @@ EXPECT_TRUE( AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent)); + ResolveAudioFocusSuccess(); EXPECT_EQ(kDefaultVolumeMultiplier, player_observer->GetVolumeMultiplier(0)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, CanRequestFocusBeforePlayerCreation) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); media_session_->RequestSystemAudioFocus(AudioFocusType::kGain); EXPECT_TRUE(IsActive()); + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsActive()); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, StartPlayerGivesFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + StartPlayerGivesFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, SuspendGivesAwayAudioFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, StopGivesAwayAudioFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + StopGivesAwayAudioFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); media_session_->Stop(MediaSession::SuspendType::kUI); EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ResumeGivesBackAudioFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + SystemResumeGivesBackAudioFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); SystemResume(); @@ -415,13 +503,30 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + UIResumeGivesBackAudioFocus) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); + + UISuspend(); + + UIResume(); + EXPECT_TRUE(IsActive()); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsActive()); +} + +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, RemovingLastPlayerDropsAudioFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); EXPECT_TRUE(IsActive()); @@ -431,7 +536,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, RemovingLastPlayerFromManyObserversDropsAudioFocus) { auto player_observer_1 = std::make_unique<MockMediaSessionPlayerObserver>(); auto player_observer_2 = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -440,6 +545,7 @@ StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_3.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer_1.get(), 0); EXPECT_TRUE(IsActive()); @@ -449,7 +555,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, RemovingAllPlayersFromObserversDropsAudioFocus) { auto player_observer_1 = std::make_unique<MockMediaSessionPlayerObserver>(); auto player_observer_2 = std::make_unique<MockMediaSessionPlayerObserver>(); @@ -458,6 +564,7 @@ StartNewPlayer(player_observer_1.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer_2.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayers(player_observer_1.get()); EXPECT_TRUE(IsActive()); @@ -465,29 +572,40 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ResumePlayGivesAudioFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + ResumePlayGivesAudioFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); EXPECT_FALSE(IsActive()); EXPECT_TRUE( AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent)); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ResumeSuspendSeekAreSentOnlyOncePerPlayers) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); EXPECT_EQ(0, player_observer->received_suspend_calls()); EXPECT_EQ(0, player_observer->received_resume_calls()); + + ResolveAudioFocusSuccess(); + + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); EXPECT_EQ(0, player_observer->received_seek_forward_calls()); EXPECT_EQ(0, player_observer->received_seek_backward_calls()); @@ -504,14 +622,22 @@ EXPECT_EQ(3, player_observer->received_seek_backward_calls()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ResumeSuspendSeekAreSentOnlyOncePerPlayersAddedTwice) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_EQ(0, player_observer->received_suspend_calls()); + EXPECT_EQ(0, player_observer->received_resume_calls()); + + ResolveAudioFocusSuccess(); + // Adding the three players above again. EXPECT_TRUE( AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent)); @@ -538,33 +664,38 @@ EXPECT_EQ(3, player_observer->received_seek_backward_calls()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, RemovingTheSamePlayerTwiceIsANoop) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); RemovePlayer(player_observer.get(), 0); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, AudioFocusType) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, AudioFocusType) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); // Starting a player with a given type should set the session to that type. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + ResolveAudioFocusSuccess(); EXPECT_EQ(AudioFocusType::kGainTransientMayDuck, GetSessionAudioFocusType()); // Adding a player of the same type should have no effect on the type. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); EXPECT_EQ(AudioFocusType::kGainTransientMayDuck, GetSessionAudioFocusType()); // Adding a player of Content type should override the current type. StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_EQ(AudioFocusType::kGain, GetSessionAudioFocusType()); // Adding a player of the Transient type should have no effect on the type. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); EXPECT_EQ(AudioFocusType::kGain, GetSessionAudioFocusType()); EXPECT_TRUE(player_observer->IsPlaying(0)); @@ -591,7 +722,8 @@ EXPECT_EQ(AudioFocusType::kGain, GetSessionAudioFocusType()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ControlsShowForContent) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + ControlsShowForContent) { EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -599,12 +731,13 @@ // Starting a player with a content type should show the media controls. StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsNoShowForTransient) { EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(false, false)); @@ -613,12 +746,14 @@ // Starting a player with a transient type should not show the media controls. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + ResolveAudioFocusSuccess(); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ControlsHideWhenStopped) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + ControlsHideWhenStopped) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); EXPECT_CALL(*mock_media_session_observer(), @@ -628,6 +763,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayers(player_observer.get()); @@ -635,7 +771,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsShownAcceptTransient) { EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -643,6 +779,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); // Transient player join the session without affecting the controls. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); @@ -651,7 +788,7 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsShownAfterContentAdded) { Expectation dontShowControls = EXPECT_CALL( *mock_media_session_observer(), MediaSessionStateChanged(false, false)); @@ -662,15 +799,17 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + ResolveAudioFocusSuccess(); // The controls are shown when the content player is added. StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsStayIfOnlyOnePlayerHasBeenPaused) { EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -678,6 +817,8 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); // Removing only content player doesn't hide the controls since the session @@ -688,7 +829,7 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsHideWhenTheLastPlayerIsRemoved) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -700,6 +841,7 @@ StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); @@ -712,7 +854,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsHideWhenAllThePlayersAreRemoved) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -724,6 +866,7 @@ StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayers(player_observer.get()); @@ -731,7 +874,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsNotHideWhenTheLastPlayerIsPaused) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -743,6 +886,7 @@ StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); OnPlayerPaused(player_observer.get(), 0); @@ -755,7 +899,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, SuspendTemporaryUpdatesControls) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -766,6 +910,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); @@ -773,7 +918,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsUpdatedWhenResumed) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -787,6 +932,8 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); + SystemSuspend(true); SystemResume(); @@ -794,7 +941,7 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsHideWhenSessionSuspendedPermanently) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -805,6 +952,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(false); @@ -812,8 +960,8 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, - ConstrolsHideWhenSessionStops) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + ControlsHideWhenSessionStops) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); Expectation pauseControls = EXPECT_CALL(*mock_media_session_observer(), @@ -826,6 +974,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); media_session_->Stop(MediaSession::SuspendType::kUI); @@ -833,7 +982,7 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsHideWhenSessionChangesFromContentToTransient) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -847,17 +996,19 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); // This should reset the session and change it to a transient, so // hide the controls. StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + ResolveAudioFocusSuccess(); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsUpdatedWhenNewPlayerResetsSession) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -871,16 +1022,18 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); // This should reset the session and update the controls. StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsResumedWhenPlayerIsResumed) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -894,16 +1047,18 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); // This should resume the session and update the controls. AddPlayer(player_observer.get(), 0, media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsUpdatedDueToResumeSessionAction) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -914,13 +1069,14 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsUpdatedDueToSuspendSessionAction) { Expectation showControls = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(true, false)); @@ -934,14 +1090,19 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); - UIResume(); + UIResume(); + EXPECT_TRUE(IsControllable()); + EXPECT_TRUE(IsActive()); + + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsDontShowWhenOneShotIsPresent) { EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(false, false)); @@ -949,6 +1110,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); + ResolveAudioFocusSuccess(); EXPECT_FALSE(IsControllable()); EXPECT_TRUE(IsActive()); @@ -962,7 +1124,7 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsHiddenAfterRemoveOneShotWithoutOtherPlayers) { Expectation expect_1 = EXPECT_CALL(*mock_media_session_observer(), MediaSessionStateChanged(false, false)); @@ -976,13 +1138,14 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); EXPECT_FALSE(IsControllable()); EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ControlsShowAfterRemoveOneShotWithPersistentPresent) { Expectation uncontrollable = EXPECT_CALL( *mock_media_session_observer(), MediaSessionStateChanged(false, false)); @@ -996,6 +1159,7 @@ StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); @@ -1003,13 +1167,14 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, DontSuspendWhenOneShotIsPresent) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(false); @@ -1019,11 +1184,12 @@ EXPECT_EQ(0, player_observer->received_suspend_calls()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, DontResumeBySystemUISuspendedSessions) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); EXPECT_TRUE(IsControllable()); @@ -1034,80 +1200,103 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, AllowUIResumeForSystemSuspend) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsActive()); UIResume(); + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ResumeSuspendFromUI) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ResumeSuspendFromUI) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsActive()); UIResume(); + EXPECT_TRUE(IsActive()); + + ResolveAudioFocusSuccess(); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, ResumeSuspendFromSystem) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + ResumeSuspendFromSystem) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); EXPECT_TRUE(IsControllable()); EXPECT_FALSE(IsActive()); SystemResume(); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); EXPECT_TRUE(IsControllable()); EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, OneShotTakesGainFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + OneShotTakesGainFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); + ResolveAudioFocusSuccess(); + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); EXPECT_EQ(AudioFocusType::kGain, mock_audio_focus_delegate()->GetCurrentFocusType()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, RemovingOneShotDropsFocus) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + RemovingOneShotDropsFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); EXPECT_CALL(*mock_audio_focus_delegate(), AbandonAudioFocus()); StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); + ResolveAudioFocusSuccess(); + RemovePlayer(player_observer.get(), 0); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, RemovingOneShotWhileStillHavingOtherPlayersKeepsFocus) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); EXPECT_CALL(*mock_audio_focus_delegate(), AbandonAudioFocus()) .Times(1); // Called in TearDown StartNewPlayer(player_observer.get(), media::MediaContentType::OneShot); + ResolveAudioFocusSuccess(); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_FALSE(HasUnresolvedAudioFocusRequest()); + RemovePlayer(player_observer.get(), 0); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ActualPlaybackStateWhilePlayerPaused) { EnsureMediaSessionService(); auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( @@ -1131,6 +1320,8 @@ .InSequence(s); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); + OnPlayerPaused(player_observer.get(), 0); SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PLAYING); SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PAUSED); @@ -1141,7 +1332,7 @@ ::testing::Mock::VerifyAndClear(mock_media_session_observer()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ActualPlaybackStateWhilePlayerPlaying) { EnsureMediaSessionService(); auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( @@ -1161,6 +1352,8 @@ .InSequence(s); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); + SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PLAYING); SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PAUSED); SetPlaybackState(blink::mojom::MediaSessionPlaybackState::NONE); @@ -1170,7 +1363,7 @@ ::testing::Mock::VerifyAndClear(mock_media_session_observer()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, ActualPlaybackStateWhilePlayerRemoved) { EnsureMediaSessionService(); auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( @@ -1185,6 +1378,7 @@ .InSequence(s); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); RemovePlayer(player_observer.get(), 0); SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PLAYING); @@ -1196,12 +1390,13 @@ ::testing::Mock::VerifyAndClear(mock_media_session_observer()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_Suspended_SystemTransient) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(true); std::unique_ptr<base::HistogramSamples> samples( @@ -1212,12 +1407,13 @@ EXPECT_EQ(0, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_Suspended_SystemPermantent) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); SystemSuspend(false); std::unique_ptr<base::HistogramSamples> samples( @@ -1228,12 +1424,13 @@ EXPECT_EQ(0, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, UMA_Suspended_UI) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_Suspended_UI) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); std::unique_ptr<base::HistogramSamples> samples( @@ -1244,20 +1441,24 @@ EXPECT_EQ(1, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, UMA_Suspended_Multiple) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + UMA_Suspended_Multiple) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); UIResume(); + ResolveAudioFocusSuccess(); SystemSuspend(true); SystemResume(); UISuspend(); UIResume(); + ResolveAudioFocusSuccess(); SystemSuspend(false); @@ -1269,16 +1470,19 @@ EXPECT_EQ(2, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, UMA_Suspended_Crossing) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, + UMA_Suspended_Crossing) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); UISuspend(); SystemSuspend(true); SystemSuspend(false); UIResume(); + ResolveAudioFocusSuccess(); SystemSuspend(true); SystemSuspend(true); @@ -1293,11 +1497,12 @@ EXPECT_EQ(1, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, UMA_Suspended_Stop) { +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_Suspended_Stop) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); media_session_->Stop(MediaSession::SuspendType::kUI); std::unique_ptr<base::HistogramSamples> samples( @@ -1308,7 +1513,7 @@ EXPECT_EQ(1, samples->GetCount(2)); // UI } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_NoActivation) { base::HistogramTester tester; @@ -1321,7 +1526,7 @@ EXPECT_EQ(0, samples->TotalCount()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_SimpleActivation) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1332,6 +1537,7 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(1000)); media_session_->Stop(MediaSession::SuspendType::kUI); @@ -1342,7 +1548,7 @@ EXPECT_EQ(1, samples->GetCount(1000)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_ActivationWithUISuspension) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1353,12 +1559,14 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(1000)); UISuspend(); clock.Advance(base::TimeDelta::FromMilliseconds(2000)); UIResume(); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(1000)); media_session_->Stop(MediaSession::SuspendType::kUI); @@ -1369,7 +1577,7 @@ EXPECT_EQ(1, samples->GetCount(2000)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_ActivationWithSystemSuspension) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1380,6 +1588,7 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(1000)); SystemSuspend(true); @@ -1396,7 +1605,7 @@ EXPECT_EQ(1, samples->GetCount(2000)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_ActivateSuspendedButNotStopped) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1407,6 +1616,7 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(500)); SystemSuspend(true); @@ -1427,7 +1637,7 @@ } } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_ActivateSuspendStopTwice) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1438,11 +1648,13 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(500)); SystemSuspend(true); media_session_->Stop(MediaSession::SuspendType::kUI); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(5000)); SystemResume(); media_session_->Stop(MediaSession::SuspendType::kUI); @@ -1454,7 +1666,7 @@ EXPECT_EQ(1, samples->GetCount(5000)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, UMA_ActiveTime_MultipleActivations) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); base::HistogramTester tester; @@ -1465,10 +1677,12 @@ media_session_uma_helper->SetClockForTest(&clock); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(10000)); RemovePlayer(player_observer.get(), 0); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); clock.Advance(base::TimeDelta::FromMilliseconds(1000)); media_session_->Stop(MediaSession::SuspendType::kUI); @@ -1479,7 +1693,7 @@ EXPECT_EQ(1, samples->GetCount(10000)); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, AddingObserverNotifiesCurrentInformation_EmptyInfo) { media_session_->RemoveObserver(mock_media_session_observer()); EXPECT_CALL(*mock_media_session_observer(), @@ -1492,7 +1706,7 @@ media_session_->AddObserver(mock_media_session_observer()); } -IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, +IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, AddingObserverNotifiesCurrentInformation_WithInfo) { // Set up the service and information. EnsureMediaSessionService(); @@ -1512,6 +1726,7 @@ auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( shell()->web_contents()->GetMainFrame()); StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + ResolveAudioFocusSuccess(); // Check if the expectations are met when the observer is newly added. media_session_->RemoveObserver(mock_media_session_observer()); @@ -1523,3 +1738,217 @@ MediaSessionActionsChanged(Eq(expectedActions))); media_session_->AddObserver(mock_media_session_observer()); } + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_RequestFailure_Gain) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(IsActive()); + + // The gain request failed so we should suspend the whole session. + ResolveAudioFocusFailure(); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); + EXPECT_FALSE(IsActive()); + + ResolveAudioFocusSuccess(); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); + EXPECT_FALSE(IsActive()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, + Async_RequestFailure_GainTransient) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(IsActive()); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + EXPECT_TRUE(IsActive()); + + // A transient audio focus failure should only affect transient players. + ResolveAudioFocusFailure(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); + EXPECT_TRUE(IsActive()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_GainThenTransient) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_TransientThenGain) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + EXPECT_TRUE(player_observer->IsPlaying(1)); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, + Async_SuspendBeforeResolve) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + SystemSuspend(true); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(IsActive()); + + ResolveAudioFocusSuccess(); + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(IsActive()); + + SystemResume(); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_ResumeBeforeResolve) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + UISuspend(); + EXPECT_FALSE(IsActive()); + EXPECT_FALSE(player_observer->IsPlaying(0)); + + UIResume(); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + ResolveAudioFocusFailure(); + EXPECT_FALSE(IsActive()); + EXPECT_FALSE(player_observer->IsPlaying(0)); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_RemoveBeforeResolve) { + { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + EXPECT_CALL(*mock_audio_focus_delegate(), AbandonAudioFocus()); + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + RemovePlayer(player_observer.get(), 0); + } + + ResolveAudioFocusSuccess(); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_StopBeforeResolve) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Transient); + ResolveAudioFocusSuccess(); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(player_observer->IsPlaying(1)); + + media_session_->Stop(MediaSession::SuspendType::kUI); + ResolveAudioFocusSuccess(); + + EXPECT_FALSE(player_observer->IsPlaying(0)); + EXPECT_FALSE(player_observer->IsPlaying(1)); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_Unducking_Failure) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + SystemStartDucking(); + EXPECT_TRUE(IsDucking()); + + ResolveAudioFocusFailure(); + EXPECT_TRUE(IsDucking()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_Unducking_Inactive) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + media_session_->Stop(MediaSession::SuspendType::kUI); + SystemStartDucking(); + EXPECT_TRUE(IsDucking()); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsDucking()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_Unducking_Success) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + SystemStartDucking(); + EXPECT_TRUE(IsDucking()); + + ResolveAudioFocusSuccess(); + EXPECT_FALSE(IsDucking()); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest, Async_Unducking_Suspended) { + auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); + + StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent); + EXPECT_TRUE(IsActive()); + EXPECT_TRUE(player_observer->IsPlaying(0)); + + UISuspend(); + SystemStartDucking(); + EXPECT_TRUE(IsDucking()); + + ResolveAudioFocusSuccess(); + EXPECT_TRUE(IsDucking()); +}
diff --git a/content/browser/media/session/media_session_impl_unittest.cc b/content/browser/media/session/media_session_impl_unittest.cc new file mode 100644 index 0000000..176c21e9 --- /dev/null +++ b/content/browser/media/session/media_session_impl_unittest.cc
@@ -0,0 +1,298 @@ +// 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 "content/browser/media/session/media_session_impl.h" + +#include <memory> + +#include "base/command_line.h" +#include "build/build_config.h" +#include "content/browser/media/session/audio_focus_test_util.h" +#include "content/browser/media/session/media_session_player_observer.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/test_web_contents.h" +#include "media/base/media_content_type.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/media_session/public/cpp/switches.h" +#include "services/media_session/public/mojom/media_session.mojom.h" + +namespace content { + +using media_session::mojom::AudioFocusType; +using media_session::mojom::MediaSessionInfo; +using media_session::mojom::MediaSessionInfoPtr; + +namespace { + +class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver { + public: + void OnSuspend(int player_id) override {} + void OnResume(int player_id) override {} + void OnSeekForward(int player_id, base::TimeDelta seek_time) override {} + void OnSeekBackward(int player_id, base::TimeDelta seek_time) override {} + void OnSetVolumeMultiplier(int player_id, double volume_multiplier) override { + } + RenderFrameHost* render_frame_host() const override { return nullptr; } +}; + +class MockMediaSessionMojoObserver + : public media_session::mojom::MediaSessionObserver { + public: + explicit MockMediaSessionMojoObserver(MediaSessionImpl* media_session) + : binding_(this) { + media_session::mojom::MediaSessionObserverPtr observer; + binding_.Bind(mojo::MakeRequest(&observer)); + media_session->AddObserver(std::move(observer)); + } + + void MediaSessionInfoChanged(MediaSessionInfoPtr session) override { + session_info_ = std::move(session); + } + + MediaSessionInfoPtr session_info_; + + private: + mojo::Binding<media_session::mojom::MediaSessionObserver> binding_; +}; + +} // anonymous namespace + +class MediaSessionImplTest : public testing::Test { + public: + MediaSessionImplTest() = default; + + void SetUp() override { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + media_session::switches::kEnableAudioFocus); + + rph_factory_.reset(new MockRenderProcessHostFactory()); + RenderProcessHostImpl::set_render_process_host_factory_for_testing( + rph_factory_.get()); + browser_context_.reset(new TestBrowserContext()); + pepper_observer_.reset(new MockMediaSessionPlayerObserver()); + } + + void TearDown() override { + browser_context_.reset(); + RenderProcessHostImpl::set_render_process_host_factory_for_testing(nullptr); + rph_factory_.reset(); + } + + void RequestAudioFocus(MediaSessionImpl* session, + AudioFocusType audio_focus_type) { + session->RequestSystemAudioFocus(audio_focus_type); + } + + void AbandonAudioFocus(MediaSessionImpl* session) { + session->AbandonSystemAudioFocusIfNeeded(); + } + + bool GetForceDuck(MediaSessionImpl* session) { + return test::GetMediaSessionInfoSync(session)->force_duck; + } + + MediaSessionInfo::SessionState GetState(MediaSessionImpl* session) { + return test::GetMediaSessionInfoSync(session)->state; + } + + bool HasMojoObservers(MediaSessionImpl* session) { + return !session->mojo_observers_.empty(); + } + + void FlushForTesting(MediaSessionImpl* session) { + session->FlushForTesting(); + } + + std::unique_ptr<WebContents> CreateWebContents() { + return TestWebContents::Create( + browser_context_.get(), SiteInstance::Create(browser_context_.get())); + } + + std::unique_ptr<MediaSessionPlayerObserver> pepper_observer_; + + private: + TestBrowserThreadBundle test_browser_thread_bundle_; + + std::unique_ptr<MockRenderProcessHostFactory> rph_factory_; + std::unique_ptr<TestBrowserContext> browser_context_; +}; + +TEST_F(MediaSessionImplTest, SessionInfoState) { + std::unique_ptr<WebContents> web_contents(CreateWebContents()); + MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, GetState(media_session)); + + { + MockMediaSessionMojoObserver observer(media_session); + RequestAudioFocus(media_session, AudioFocusType::kGain); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } + + { + MockMediaSessionMojoObserver observer(media_session); + media_session->StartDucking(); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } + + { + MockMediaSessionMojoObserver observer(media_session); + media_session->StopDucking(); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } + + { + MockMediaSessionMojoObserver observer(media_session); + media_session->Suspend(MediaSession::SuspendType::kSystem); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kSuspended, + GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } + + { + MockMediaSessionMojoObserver observer(media_session); + media_session->Resume(MediaSession::SuspendType::kSystem); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } + + { + MockMediaSessionMojoObserver observer(media_session); + AbandonAudioFocus(media_session); + FlushForTesting(media_session); + + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, + GetState(media_session)); + EXPECT_TRUE(observer.session_info_.Equals( + test::GetMediaSessionInfoSync(media_session))); + } +} + +TEST_F(MediaSessionImplTest, PepperForcesDuckAndRequestsFocus) { + std::unique_ptr<WebContents> web_contents(CreateWebContents()); + MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + + media_session->AddPlayer(pepper_observer_.get(), 0, + media::MediaContentType::Pepper); + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + EXPECT_TRUE(GetForceDuck(media_session)); + + media_session->RemovePlayer(pepper_observer_.get(), 0); + EXPECT_EQ(MediaSessionInfo::SessionState::kInactive, GetState(media_session)); + EXPECT_FALSE(GetForceDuck(media_session)); +} + +TEST_F(MediaSessionImplTest, RegisterMojoObserver) { + std::unique_ptr<WebContents> web_contents(CreateWebContents()); + MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + + EXPECT_FALSE(HasMojoObservers(media_session)); + + MockMediaSessionMojoObserver observer(media_session); + FlushForTesting(media_session); + + EXPECT_TRUE(HasMojoObservers(media_session)); +} + +#if !defined(OS_ANDROID) + +TEST_F(MediaSessionImplTest, WebContentsDestroyed_ReleasesFocus) { + std::unique_ptr<WebContents> web_contents(CreateWebContents()); + MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + + { + test::TestAudioFocusObserver observer; + RequestAudioFocus(media_session, AudioFocusType::kGain); + observer.WaitForGainedEvent(); + } + + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + + { + test::TestAudioFocusObserver observer; + web_contents.reset(); + observer.WaitForLostEvent(); + } +} + +TEST_F(MediaSessionImplTest, WebContentsDestroyed_ReleasesTransients) { + std::unique_ptr<WebContents> web_contents(CreateWebContents()); + MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents.get()); + + { + test::TestAudioFocusObserver observer; + RequestAudioFocus(media_session, AudioFocusType::kGainTransientMayDuck); + observer.WaitForGainedEvent(); + } + + EXPECT_EQ(MediaSessionInfo::SessionState::kActive, GetState(media_session)); + + { + test::TestAudioFocusObserver observer; + web_contents.reset(); + observer.WaitForLostEvent(); + } +} + +TEST_F(MediaSessionImplTest, WebContentsDestroyed_StopsDucking) { + std::unique_ptr<WebContents> web_contents_1(CreateWebContents()); + MediaSessionImpl* media_session_1 = + MediaSessionImpl::Get(web_contents_1.get()); + + std::unique_ptr<WebContents> web_contents_2(CreateWebContents()); + MediaSessionImpl* media_session_2 = + MediaSessionImpl::Get(web_contents_2.get()); + + { + test::TestAudioFocusObserver observer; + RequestAudioFocus(media_session_1, AudioFocusType::kGain); + observer.WaitForGainedEvent(); + } + + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(media_session_1)); + + { + test::TestAudioFocusObserver observer; + RequestAudioFocus(media_session_2, AudioFocusType::kGainTransientMayDuck); + observer.WaitForGainedEvent(); + } + + EXPECT_EQ(MediaSessionInfo::SessionState::kDucking, + GetState(media_session_1)); + + { + test::TestAudioFocusObserver observer; + web_contents_2.reset(); + observer.WaitForLostEvent(); + } + + EXPECT_NE(MediaSessionInfo::SessionState::kDucking, + GetState(media_session_1)); +} + +#endif // !defined(OS_ANDROID) + +} // namespace content
diff --git a/content/browser/process_internals/process_internals_ui.cc b/content/browser/process_internals/process_internals_ui.cc index 8c3558c..0394d232 100644 --- a/content/browser/process_internals/process_internals_ui.cc +++ b/content/browser/process_internals/process_internals_ui.cc
@@ -35,6 +35,7 @@ WebUIDataSource::Create(kChromeUIProcessInternalsHost); source->AddResourcePath("process_internals.js", IDR_PROCESS_INTERNALS_JS); + source->AddResourcePath("process_internals.css", IDR_PROCESS_INTERNALS_CSS); source->AddResourcePath("process_internals.mojom.js", IDR_PROCESS_INTERNALS_MOJO_JS); source->SetDefaultResource(IDR_PROCESS_INTERNALS_HTML);
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 0e6b97a..ac24be9a 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2236,10 +2236,18 @@ &RenderProcessHostImpl::CreateRendererHost, base::Unretained(this))); if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + // Using an opaque origin here should be safe - the URLLoaderFactory created + // for such origin shouldn't have any special privileges. + // + // TODO(lukasza): https://crbug.com/871827: Use the actual origin that will + // be used as |request_initiator|. The origin should come from the browser + // process. + const url::Origin kSafeOrigin = url::Origin(); + AddUIThreadInterface( registry.get(), base::Bind(&RenderProcessHostImpl::CreateURLLoaderFactory, - base::Unretained(this))); + base::Unretained(this), kSafeOrigin)); } registry->AddInterface( @@ -2516,6 +2524,7 @@ } void RenderProcessHostImpl::CreateURLLoaderFactory( + const url::Origin& origin, network::mojom::URLLoaderFactoryRequest request) { if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { base::PostTaskWithTraits( @@ -2524,15 +2533,26 @@ std::move(request))); return; } - network::mojom::URLLoaderFactoryParamsPtr params = - network::mojom::URLLoaderFactoryParams::New(); - params->process_id = id_; - params->disable_web_security = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableWebSecurity); - SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get()); - storage_partition_impl_->GetNetworkContext()->CreateURLLoaderFactory( - std::move(request), std::move(params)); + + network::mojom::NetworkContext* network_context = + storage_partition_impl_->GetNetworkContext(); + network::mojom::URLLoaderFactoryPtrInfo embedder_provided_factory = + GetContentClient()->browser()->CreateURLLoaderFactoryForNetworkRequests( + this, network_context, origin); + if (embedder_provided_factory) { + mojo::FuseInterface(std::move(request), + std::move(embedder_provided_factory)); + } else { + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = GetID(); + params->disable_web_security = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableWebSecurity); + SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get()); + network_context->CreateURLLoaderFactory(std::move(request), + std::move(params)); + } } void RenderProcessHostImpl::SetIsNeverSuitableForReuse() {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index bba58ee..bd316ca5 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -218,6 +218,7 @@ resource_coordinator::ProcessResourceCoordinator* GetProcessResourceCoordinator() override; void CreateURLLoaderFactory( + const url::Origin& origin, network::mojom::URLLoaderFactoryRequest request) override; void SetIsNeverSuitableForReuse() override;
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 61220b8..b2857d65 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -269,6 +269,10 @@ class UnboundWidgetInputHandler : public mojom::WidgetInputHandler { public: + void FlushForTesting(FlushForTestingCallback callback) override { + CHECK(false) << "Flush for testing called on an unbound interface."; + } + void SetFocus(bool focused) override { DLOG(WARNING) << "Input request on unbound interface"; } @@ -981,6 +985,12 @@ focused_widget->SetPageFocus(false); } +void RenderWidgetHostImpl::FlushForTesting(FlushForTestingCallback callback) { + // Flush IPC messages on WidgetInputHandler to ensure that SetFocus() messages + // have been processed. + GetWidgetInputHandler()->FlushForTesting(std::move(callback)); +} + void RenderWidgetHostImpl::SetPageFocus(bool focused) { is_focused_ = focused;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 9f16c81..c2257b5 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -187,6 +187,7 @@ void NotifyTextDirection() override; void Focus() override; void Blur() override; + void FlushForTesting(FlushForTestingCallback callback) override; void SetActive(bool active) override; void ForwardMouseEvent(const blink::WebMouseEvent& mouse_event) override; void ForwardWheelEvent(const blink::WebMouseWheelEvent& wheel_event) override;
diff --git a/content/browser/resources/process/process_internals.css b/content/browser/resources/process/process_internals.css new file mode 100644 index 0000000..0fbc588a --- /dev/null +++ b/content/browser/resources/process/process_internals.css
@@ -0,0 +1,82 @@ +/* Copyright 2018 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +* { + box-sizing: border-box; +} + +html { + height: 100%; +} + +body { + color: rgb(48, 57, 66); + display: flex; + flex-direction: column; + font-size: 13px; + height: 100%; + margin: 0; + overflow: auto; +} + +#container { + display: flex; + height: 100% +} + +#navigation { + flex-shrink: 0; + padding-top: 20px; + width: 200px; +} + +#content { + flex-grow: 1; +} + +#caption { + color: rgb(92, 97, 102); + font-size: 1.5rem; + padding-bottom: 10px; + padding-inline-start: 20px; +} + +.tab-header { + -webkit-border-start: 6px solid transparent; + padding-left: 15px; +} + +.tab-header.selected { + -webkit-border-start-color: rgb(78, 87, 100); +} + +.tab-header > button { + background-color: white; + border: 0; + cursor: pointer; + font: inherit; + line-height: 17px; + margin: 6px 0; + padding: 0 2px; +} + +.tab-header:not(.selected) > button { + color: #999; +} + +#content > div { + min-width: 32em; + padding: 0 20px 65px 0; +} +#content > div:not(.selected) { + display: none; +} + +.content-header { + background: linear-gradient(white, white 40%, rgba(255, 255, 255, 0.92)); + border-bottom: 1px solid #eee; + font-size: 150%; + padding: 20px 0 10px 0; + z-index: 1; +}
diff --git a/content/browser/resources/process/process_internals.html b/content/browser/resources/process/process_internals.html index 9b63631e..36d2fd2 100644 --- a/content/browser/resources/process/process_internals.html +++ b/content/browser/resources/process/process_internals.html
@@ -6,11 +6,33 @@ <head> <meta charset="utf-8"> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> + <link rel="stylesheet" href="process_internals.css"> <script src="chrome://resources/js/mojo_bindings.js"></script> + <script src="chrome://resources/js/util.js"></script> <script src="process_internals.mojom.js"></script> <script src='process_internals.js'></script> <title>Process Model Internals</title> </head> -<div id="site-isolation-mode">Site Isolation mode: <span id='isolation-mode'>unknown</span></div> -<div id="isolated-origins-container">Number of isolated origins: <span id='isolated-origins'></span></div> +<body> + +<div id="container"> + <div id="navigation"> + <div id="caption">Process Internals</div> + </div> + <div id="content"> + <div id="general"> + <div class="content-header">General info</div> + <div id="general-info"> + <div id="site-isolation-mode">Site Isolation mode: <span id='isolation-mode'>unknown</span></div> + <div id="isolated-origins-container">Number of isolated origins: <span id='isolated-origins'></span></div> + </div> + </div> + <div id="web-contents"> + <div class="content-header">WebContents</div> + <div id="wc-list" class="list pages"></div> + </div> + </div> +</div> + +</body> </html>
diff --git a/content/browser/resources/process/process_internals.js b/content/browser/resources/process/process_internals.js index 1558474..d9181a2 100644 --- a/content/browser/resources/process/process_internals.js +++ b/content/browser/resources/process/process_internals.js
@@ -2,16 +2,61 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -console.log('process internals initializing'); - (function() { 'use strict'; /** - * Reference to the backend. + * Reference to the backend providing all the data. * @type {mojom.ProcessInternalsHandlerPtr} */ -var uiHandler = null; +let uiHandler = null; + +/** + * @param {string} id Tab id. + * @return {boolean} True if successful. + */ +function selectTab(id) { + const tabContents = document.querySelectorAll('#content > div'); + const tabHeaders = $('navigation').querySelectorAll('.tab-header'); + let found = false; + for (let i = 0; i < tabContents.length; i++) { + const tabContent = tabContents[i]; + const tabHeader = tabHeaders[i]; + const isTargetTab = tabContent.id == id; + + found = found || isTargetTab; + tabContent.classList.toggle('selected', isTargetTab); + tabHeader.classList.toggle('selected', isTargetTab); + } + if (!found) + return false; + window.location.hash = id; + return true; +} + +function onHashChange() { + let hash = window.location.hash.slice(1).toLowerCase(); + if (!selectTab(hash)) + selectTab('general'); +} + +function setupTabs() { + const tabContents = document.querySelectorAll('#content > div'); + for (let i = 0; i < tabContents.length; i++) { + const tabContent = tabContents[i]; + const tabName = tabContent.querySelector('.content-header').textContent; + + let tabHeader = document.createElement('div'); + tabHeader.className = 'tab-header'; + let button = document.createElement('button'); + button.textContent = tabName; + tabHeader.appendChild(button); + tabHeader.addEventListener('click', selectTab.bind(null, tabContent.id)); + $('navigation').appendChild(tabHeader); + } + onHashChange(); +} + document.addEventListener('DOMContentLoaded', function() { // Setup Mojo interface to the backend. @@ -27,6 +72,9 @@ uiHandler.getIsolatedOriginsSize().then((response) => { document.getElementById('isolated-origins').innerText = response.size; }); + + // Setup the UI + setupTabs(); }); })();
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc index df32985..0413d3f 100644 --- a/content/browser/security_exploit_browsertest.cc +++ b/content/browser/security_exploit_browsertest.cc
@@ -239,26 +239,27 @@ } static void CreateLoaderAndStart( - RenderProcessHost* process, + RenderFrameHost* frame, int route_id, int request_id, const network::ResourceRequest& resource_request) { network::mojom::URLLoaderPtr loader; network::TestURLLoaderClient client; - CreateLoaderAndStart(process, mojo::MakeRequest(&loader), route_id, + CreateLoaderAndStart(frame, mojo::MakeRequest(&loader), route_id, request_id, resource_request, client.CreateInterfacePtr().PassInterface()); } static void CreateLoaderAndStart( - RenderProcessHost* process, + RenderFrameHost* frame, network::mojom::URLLoaderRequest request, int route_id, int request_id, const network::ResourceRequest& resource_request, network::mojom::URLLoaderClientPtrInfo client) { network::mojom::URLLoaderFactoryPtr factory; - process->CreateURLLoaderFactory(mojo::MakeRequest(&factory)); + frame->GetProcess()->CreateURLLoaderFactory(frame->GetLastCommittedOrigin(), + mojo::MakeRequest(&factory)); factory->CreateLoaderAndStart( std::move(request), route_id, request_id, network::mojom::kURLLoadOptionNone, resource_request, @@ -289,12 +290,12 @@ network::mojom::URLLoaderPtr loader1, loader2; network::TestURLLoaderClient client1, client2; - CreateLoaderAndStart(rfh->GetProcess(), mojo::MakeRequest(&loader1), - rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, - request, client1.CreateInterfacePtr().PassInterface()); - CreateLoaderAndStart(rfh->GetProcess(), mojo::MakeRequest(&loader2), - rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, - request, client2.CreateInterfacePtr().PassInterface()); + CreateLoaderAndStart(rfh, mojo::MakeRequest(&loader1), rfh->GetRoutingID(), + kRequestIdNotPreviouslyUsed, request, + client1.CreateInterfacePtr().PassInterface()); + CreateLoaderAndStart(rfh, mojo::MakeRequest(&loader2), rfh->GetRoutingID(), + kRequestIdNotPreviouslyUsed, request, + client2.CreateInterfacePtr().PassInterface()); EXPECT_EQ(bad_message::RDH_INVALID_REQUEST_ID, kill_waiter.Wait()); } @@ -531,7 +532,7 @@ { RenderProcessHostKillWaiter kill_waiter(web_rfh->GetProcess()); - CreateLoaderAndStart(web_rfh->GetProcess(), web_rfh->GetRoutingID(), + CreateLoaderAndStart(web_rfh, web_rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, chrome_origin_msg); EXPECT_EQ(bad_message::RDH_ILLEGAL_ORIGIN, kill_waiter.Wait()); } @@ -546,7 +547,7 @@ "Origin", "", base::Bind(&OnHttpHeaderReceived)); RenderProcessHostKillWaiter kill_waiter(web_rfh->GetProcess()); - CreateLoaderAndStart(web_rfh->GetProcess(), web_rfh->GetRoutingID(), + CreateLoaderAndStart(web_rfh, web_rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, embedder_isolated_origin_msg); EXPECT_EQ(bad_message::RDH_ILLEGAL_ORIGIN, kill_waiter.Wait()); @@ -556,7 +557,7 @@ NavigateToURL(shell(), web_url); { RenderProcessHostKillWaiter kill_waiter(web_rfh->GetProcess()); - CreateLoaderAndStart(web_rfh->GetProcess(), web_rfh->GetRoutingID(), + CreateLoaderAndStart(web_rfh, web_rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, invalid_origin_msg); EXPECT_EQ(bad_message::RDH_ILLEGAL_ORIGIN, kill_waiter.Wait()); } @@ -565,7 +566,7 @@ NavigateToURL(shell(), web_url); { RenderProcessHostKillWaiter kill_waiter(web_rfh->GetProcess()); - CreateLoaderAndStart(web_rfh->GetProcess(), web_rfh->GetRoutingID(), + CreateLoaderAndStart(web_rfh, web_rfh->GetRoutingID(), kRequestIdNotPreviouslyUsed, invalid_scheme_origin_msg); EXPECT_EQ(bad_message::RDH_ILLEGAL_ORIGIN, kill_waiter.Wait());
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc index 55a03e89..e8f2b46 100644 --- a/content/browser/service_worker/embedded_worker_instance.cc +++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -83,10 +83,11 @@ std::unique_ptr<URLLoaderFactoryBundleInfo> CreateFactoryBundle( RenderProcessHost* rph, + const url::Origin& origin, bool use_non_network_factories) { auto factory_bundle = std::make_unique<URLLoaderFactoryBundleInfo>(); network::mojom::URLLoaderFactoryPtrInfo default_factory_info; - rph->CreateURLLoaderFactory(mojo::MakeRequest(&default_factory_info)); + rph->CreateURLLoaderFactory(origin, mojo::MakeRequest(&default_factory_info)); factory_bundle->default_factory_info() = std::move(default_factory_info); if (use_non_network_factories) { @@ -217,10 +218,11 @@ // importScripts a non-http(s) URL. bool use_non_network_factories = !params->script_url.SchemeIsHTTPOrHTTPS(); + url::Origin origin = url::Origin::Create(params->script_url); factory_bundle_for_browser = - CreateFactoryBundle(rph, use_non_network_factories); + CreateFactoryBundle(rph, origin, use_non_network_factories); factory_bundle_for_renderer = - CreateFactoryBundle(rph, use_non_network_factories); + CreateFactoryBundle(rph, origin, use_non_network_factories); } // Register to DevTools and update params accordingly.
diff --git a/content/browser/shared_worker/shared_worker_service_impl.cc b/content/browser/shared_worker/shared_worker_service_impl.cc index 1033953..00b3d6b 100644 --- a/content/browser/shared_worker/shared_worker_service_impl.cc +++ b/content/browser/shared_worker/shared_worker_service_impl.cc
@@ -105,9 +105,16 @@ // NetworkService is off. If NetworkService is on the default factory is // set in CreateScriptLoaderOnIO(). if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + // Using an opaque origin here should be safe - the URLLoaderFactory created + // for such origin shouldn't have any special privileges. Additionally, the + // origin should not be inspected at all in the legacy, non-NetworkService + // path. + const url::Origin kSafeOrigin = url::Origin(); + network::mojom::URLLoaderFactoryPtr default_factory; RenderProcessHost::FromID(process_id) - ->CreateURLLoaderFactory(mojo::MakeRequest(&default_factory)); + ->CreateURLLoaderFactory(kSafeOrigin, + mojo::MakeRequest(&default_factory)); factory_bundle->default_factory_info() = default_factory.PassInterface(); }
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index 67e8094..cabe460 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -1350,12 +1350,6 @@ switches::kDisableWebSecurity); if (g_url_loader_factory_callback_for_test.Get().is_null()) { auto request = mojo::MakeRequest(&url_loader_factory_for_browser_process_); - - if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - GetContentClient()->browser()->WillCreateURLLoaderFactory( - browser_context(), nullptr, false /* is_navigation */, url::Origin(), - &request, nullptr /* bypass_redirect_checks */); - } GetNetworkContext()->CreateURLLoaderFactory(std::move(request), std::move(params)); is_test_url_loader_factory_for_browser_process_ = false;
diff --git a/content/common/input/input_handler.mojom b/content/common/input/input_handler.mojom index 235ea92..df7e9e12 100644 --- a/content/common/input/input_handler.mojom +++ b/content/common/input/input_handler.mojom
@@ -198,6 +198,10 @@ // an input interface for an associated Widget object. See FrameInputHandler // for an interface at the frame level. interface WidgetInputHandler { + // A simple mechanism by which the caller can ensure that all previous + // messages have been flushed. + FlushForTesting() => (); + // Tells widget focus has been changed. SetFocus(bool focused);
diff --git a/content/common/url_loader_factory_bundle.cc b/content/common/url_loader_factory_bundle.cc index 03d1494..da7a5bb 100644 --- a/content/common/url_loader_factory_bundle.cc +++ b/content/common/url_loader_factory_bundle.cc
@@ -7,18 +7,37 @@ #include <utility> #include "base/logging.h" +#include "services/network/public/cpp/resource_request.h" #include "url/gurl.h" namespace content { +namespace { + +template <typename TKey> +void BindPtrInfoMapToPtrMap( + std::map<TKey, network::mojom::URLLoaderFactoryPtr>* target, + std::map<TKey, network::mojom::URLLoaderFactoryPtrInfo> input) { + for (auto& it : input) { + const TKey& key = it.first; + network::mojom::URLLoaderFactoryPtrInfo& factory_info = it.second; + (*target)[key].Bind(std::move(factory_info)); + } +} + +} // namespace + URLLoaderFactoryBundleInfo::URLLoaderFactoryBundleInfo() = default; URLLoaderFactoryBundleInfo::URLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, bool bypass_redirect_checks) : default_factory_info_(std::move(default_factory_info)), scheme_specific_factory_infos_(std::move(scheme_specific_factory_infos)), + initiator_specific_factory_infos_( + std::move(initiator_specific_factory_infos)), bypass_redirect_checks_(bypass_redirect_checks) {} URLLoaderFactoryBundleInfo::~URLLoaderFactoryBundleInfo() = default; @@ -29,6 +48,8 @@ other->default_factory_info_ = std::move(default_factory_info_); other->scheme_specific_factory_infos_ = std::move(scheme_specific_factory_infos_); + other->initiator_specific_factory_infos_ = + std::move(initiator_specific_factory_infos_); other->bypass_redirect_checks_ = bypass_redirect_checks_; return base::MakeRefCounted<URLLoaderFactoryBundle>(std::move(other)); @@ -50,12 +71,19 @@ default_factory_ = std::move(factory); } -network::mojom::URLLoaderFactory* URLLoaderFactoryBundle::GetFactoryForURL( - const GURL& url) { - auto it = scheme_specific_factories_.find(url.scheme()); +network::mojom::URLLoaderFactory* URLLoaderFactoryBundle::GetFactory( + const network::ResourceRequest& request) { + auto it = scheme_specific_factories_.find(request.url.scheme()); if (it != scheme_specific_factories_.end()) return it->second.get(); + if (request.request_initiator.has_value()) { + auto it2 = + initiator_specific_factories_.find(request.request_initiator.value()); + if (it2 != initiator_specific_factories_.end()) + return it2->second.get(); + } + return default_factory_.get(); } @@ -67,8 +95,7 @@ const network::ResourceRequest& request, network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { - network::mojom::URLLoaderFactory* factory_ptr = GetFactoryForURL(request.url); - + network::mojom::URLLoaderFactory* factory_ptr = GetFactory(request); factory_ptr->CreateLoaderAndStart(std::move(loader), routing_id, request_id, options, request, std::move(client), traffic_annotation); @@ -85,16 +112,10 @@ if (default_factory_) default_factory_->Clone(mojo::MakeRequest(&default_factory_info)); - URLLoaderFactoryBundleInfo::SchemeMap scheme_specific_factory_infos; - for (auto& factory : scheme_specific_factories_) { - network::mojom::URLLoaderFactoryPtrInfo factory_info; - factory.second->Clone(mojo::MakeRequest(&factory_info)); - scheme_specific_factory_infos.emplace(factory.first, - std::move(factory_info)); - } - return std::make_unique<URLLoaderFactoryBundleInfo>( - std::move(default_factory_info), std::move(scheme_specific_factory_infos), + std::move(default_factory_info), + ClonePtrMapToPtrInfoMap(scheme_specific_factories_), + ClonePtrMapToPtrInfoMap(initiator_specific_factories_), bypass_redirect_checks_); } @@ -106,9 +127,10 @@ std::unique_ptr<URLLoaderFactoryBundleInfo> info) { if (info->default_factory_info()) default_factory_.Bind(std::move(info->default_factory_info())); - for (auto& factory_info : info->scheme_specific_factory_infos()) - scheme_specific_factories_[factory_info.first].Bind( - std::move(factory_info.second)); + BindPtrInfoMapToPtrMap(&scheme_specific_factories_, + std::move(info->scheme_specific_factory_infos())); + BindPtrInfoMapToPtrMap(&initiator_specific_factories_, + std::move(info->initiator_specific_factory_infos())); bypass_redirect_checks_ = info->bypass_redirect_checks(); }
diff --git a/content/common/url_loader_factory_bundle.h b/content/common/url_loader_factory_bundle.h index 56048a2c..33622f2 100644 --- a/content/common/url_loader_factory_bundle.h +++ b/content/common/url_loader_factory_bundle.h
@@ -8,13 +8,17 @@ #include <map> #include <memory> #include <string> +#include <utility> #include "base/macros.h" #include "content/common/content_export.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "url/origin.h" -class GURL; +namespace network { +struct ResourceRequest; +}; namespace content { @@ -29,10 +33,17 @@ using SchemeMap = std::map<std::string, network::mojom::URLLoaderFactoryPtrInfo>; + // Map from origin of request initiator to URLLoaderFactoryPtrInfo for + // handling this initiator's requests (e.g. for relaxing CORB for requests + // initiated from content scripts). + using OriginMap = + std::map<url::Origin, network::mojom::URLLoaderFactoryPtrInfo>; + URLLoaderFactoryBundleInfo(); URLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, bool bypass_redirect_checks); ~URLLoaderFactoryBundleInfo() override; @@ -43,6 +54,9 @@ SchemeMap& scheme_specific_factory_infos() { return scheme_specific_factory_infos_; } + OriginMap& initiator_specific_factory_infos() { + return initiator_specific_factory_infos_; + } bool bypass_redirect_checks() const { return bypass_redirect_checks_; } void set_bypass_redirect_checks(bool bypass_redirect_checks) { @@ -55,6 +69,7 @@ network::mojom::URLLoaderFactoryPtrInfo default_factory_info_; SchemeMap scheme_specific_factory_infos_; + OriginMap initiator_specific_factory_infos_; bool bypass_redirect_checks_ = false; DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryBundleInfo); @@ -94,10 +109,24 @@ protected: ~URLLoaderFactoryBundle() override; - // Returns a factory which can be used to acquire a loader for |url|. If no - // registered factory matches |url|'s scheme, the default factory is used. It - // is undefined behavior to call this when no default factory is set. - virtual network::mojom::URLLoaderFactory* GetFactoryForURL(const GURL& url); + // Returns a factory which can be used to acquire a loader for |request|. + virtual network::mojom::URLLoaderFactory* GetFactory( + const network::ResourceRequest& request); + + template <typename TKey> + static std::map<TKey, network::mojom::URLLoaderFactoryPtrInfo> + ClonePtrMapToPtrInfoMap( + const std::map<TKey, network::mojom::URLLoaderFactoryPtr>& input) { + std::map<TKey, network::mojom::URLLoaderFactoryPtrInfo> output; + for (const auto& it : input) { + const TKey& key = it.first; + const network::mojom::URLLoaderFactoryPtr& factory = it.second; + network::mojom::URLLoaderFactoryPtrInfo factory_info; + factory->Clone(mojo::MakeRequest(&factory_info)); + output.emplace(key, std::move(factory_info)); + } + return output; + } network::mojom::URLLoaderFactoryPtr default_factory_; @@ -108,6 +137,11 @@ using SchemeMap = std::map<std::string, network::mojom::URLLoaderFactoryPtr>; SchemeMap scheme_specific_factories_; + // Map from origin of request initiator to URLLoaderFactoryPtr for handling + // this initiator's requests. See also URLLoaderFactoryBundleInfo::OriginMap. + using OriginMap = std::map<url::Origin, network::mojom::URLLoaderFactoryPtr>; + OriginMap initiator_specific_factories_; + bool bypass_redirect_checks_ = false; };
diff --git a/content/common/url_loader_factory_bundle.mojom b/content/common/url_loader_factory_bundle.mojom index 0b708f8..eb99321 100644 --- a/content/common/url_loader_factory_bundle.mojom +++ b/content/common/url_loader_factory_bundle.mojom
@@ -5,6 +5,7 @@ module content.mojom; import "services/network/public/mojom/url_loader_factory.mojom"; +import "url/mojom/origin.mojom"; // Serializes a collection of URLLoaderFactory interfaces. struct URLLoaderFactoryBundle { @@ -17,6 +18,10 @@ // A mapping from URL scheme to factory interface. map<string, network.mojom.URLLoaderFactory> scheme_specific_factories; + // A mapping from request-initiator-origin to factory interface. + map<url.mojom.Origin, network.mojom.URLLoaderFactory> + initiator_specific_factories; + // Whether redirect checks should be bypassed, since they are happening in the // browser. bool bypass_redirect_checks = false;
diff --git a/content/common/url_loader_factory_bundle_struct_traits.cc b/content/common/url_loader_factory_bundle_struct_traits.cc index e6c9402b..f0236343 100644 --- a/content/common/url_loader_factory_bundle_struct_traits.cc +++ b/content/common/url_loader_factory_bundle_struct_traits.cc
@@ -7,6 +7,8 @@ #include <memory> #include <utility> +#include "url/mojom/origin_mojom_traits.h" + namespace mojo { using Traits = @@ -26,6 +28,12 @@ } // static +content::URLLoaderFactoryBundleInfo::OriginMap +Traits::initiator_specific_factories(BundleInfoType& bundle) { + return std::move(bundle->initiator_specific_factory_infos()); +} + +// static bool Traits::bypass_redirect_checks(BundleInfoType& bundle) { return bundle->bypass_redirect_checks(); } @@ -40,6 +48,9 @@ if (!data.ReadSchemeSpecificFactories( &(*out_bundle)->scheme_specific_factory_infos())) return false; + if (!data.ReadInitiatorSpecificFactories( + &(*out_bundle)->initiator_specific_factory_infos())) + return false; (*out_bundle)->set_bypass_redirect_checks(data.bypass_redirect_checks());
diff --git a/content/common/url_loader_factory_bundle_struct_traits.h b/content/common/url_loader_factory_bundle_struct_traits.h index d6d6c65..84f9bb62 100644 --- a/content/common/url_loader_factory_bundle_struct_traits.h +++ b/content/common/url_loader_factory_bundle_struct_traits.h
@@ -28,6 +28,9 @@ static content::URLLoaderFactoryBundleInfo::SchemeMap scheme_specific_factories(BundleInfoType& bundle); + static content::URLLoaderFactoryBundleInfo::OriginMap + initiator_specific_factories(BundleInfoType& bundle); + static bool bypass_redirect_checks(BundleInfoType& bundle); static bool Read(content::mojom::URLLoaderFactoryBundleDataView data,
diff --git a/content/content_resources.grd b/content/content_resources.grd index b178e346..e085e5f 100644 --- a/content/content_resources.grd +++ b/content/content_resources.grd
@@ -38,6 +38,7 @@ <include name="IDR_NETWORK_ERROR_LISTING_CSS" file="browser/resources/net/network_errors_listing.css" flattenhtml="true" type="BINDATA" /> <include name="IDR_PROCESS_INTERNALS_HTML" file="browser/resources/process/process_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> <include name="IDR_PROCESS_INTERNALS_MOJO_JS" file="${root_gen_dir}/content/browser/process_internals/process_internals.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" /> + <include name="IDR_PROCESS_INTERNALS_CSS" file="browser/resources/process/process_internals.css" flattenhtml="true" compress="gzip" type="BINDATA" /> <include name="IDR_PROCESS_INTERNALS_JS" file="browser/resources/process/process_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" /> <include name="IDR_SERVICE_WORKER_INTERNALS_HTML" file="browser/resources/service_worker/serviceworker_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> <include name="IDR_SERVICE_WORKER_INTERNALS_JS" file="browser/resources/service_worker/serviceworker_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java b/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java index 43bc0391..eaaefe3 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java
@@ -8,7 +8,7 @@ public interface JavaScriptCallback { /** * Called from native in response to evaluateJavaScript(). - * @param jsonResult json result curresponds to JS execution + * @param jsonResult json result corresponding to JS execution */ void handleJavaScriptResult(String jsonResult); }
diff --git a/content/public/browser/browser_task_traits.h b/content/public/browser/browser_task_traits.h index c370cec..e5a537cc 100644 --- a/content/public/browser/browser_task_traits.h +++ b/content/public/browser/browser_task_traits.h
@@ -41,13 +41,20 @@ // Posting to a BrowserThread must only be done after it was initialized (ref. // BrowserMainLoop::CreateThreads() phase). class CONTENT_EXPORT BrowserTaskTraitsExtension { + using BrowserThreadIDFilter = + base::trait_helpers::RequiredEnumTraitFilter<BrowserThread::ID>; + using NonNestableFilter = + base::trait_helpers::BooleanTraitFilter<NonNestable>; + public: static constexpr uint8_t kExtensionId = base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; - struct ValidTrait { - ValidTrait(BrowserThread::ID) {} - ValidTrait(NonNestable) {} + struct ValidTrait : public base::TaskTraits::ValidTrait { + using base::TaskTraits::ValidTrait::ValidTrait; + + ValidTrait(BrowserThread::ID); + ValidTrait(NonNestable); }; template < @@ -55,11 +62,10 @@ class CheckArgumentsAreValid = std::enable_if_t< base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> constexpr BrowserTaskTraitsExtension(ArgTypes... args) - : browser_thread_(base::trait_helpers::GetValueFromArgList( - base::trait_helpers::RequiredEnumArgGetter<BrowserThread::ID>(), - args...)), - nestable_(!base::trait_helpers::GetValueFromArgList( - base::trait_helpers::BooleanArgGetter<NonNestable>(), + : browser_thread_( + base::trait_helpers::GetTraitFromArgList<BrowserThreadIDFilter>( + args...)), + nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>( args...)) {} constexpr base::TaskTraitsExtensionStorage Serialize() const {
diff --git a/content/public/browser/browser_task_traits_unittest.nc b/content/public/browser/browser_task_traits_unittest.nc index 3fccbebb..e74f178 100644 --- a/content/public/browser/browser_task_traits_unittest.nc +++ b/content/public/browser/browser_task_traits_unittest.nc
@@ -10,7 +10,7 @@ namespace content { -#if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD) // [r"no member named 'GetDefaultValue' in 'base::trait_helpers::RequiredEnumArgGetter<content::BrowserThread::ID>'"] +#if defined(NCTEST_BROWSER_TASK_TRAITS_NO_THREAD) // [r"TaskTraits contains a Trait that must be explicity initialized in its constructor."] constexpr base::TaskTraits traits = {NonNestable()}; #elif defined(NCTEST_BROWSER_TASK_TRAITS_MULTIPLE_THREADS) // [r"Multiple arguments of the same type were provided to the constructor of TaskTraits."] constexpr base::TaskTraits traits = {BrowserThread::UI,
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index de9a22f..d220899 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -126,6 +126,14 @@ int render_process_id, ResourceType resource_type) {} +network::mojom::URLLoaderFactoryPtrInfo +ContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests( + RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) { + return network::mojom::URLLoaderFactoryPtrInfo(); +} + void ContentBrowserClient::GetAdditionalViewSourceSchemes( std::vector<std::string>* additional_schemes) { GetAdditionalWebUISchemes(additional_schemes);
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 236fbd71..058f398e 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -323,6 +323,28 @@ int render_process_id, ResourceType resource_type); + // Called to create a URLLoaderFactory for network requests in the following + // cases: + // - The default factory to be used by a frame. In this case + // |request_initiator| is the origin being committed in the frame (or the + // last origin committed in the frame). + // - The initiator-specific factory to be used by a frame. This happens for + // origins covered via + // RenderFrameHost::MarkInitiatorAsRequiringSeparateURLLoaderFactory. + // + // This method allows the //content embedder to provide a URLLoaderFactory + // with |request_initiator|-specific properties (e.g. with relaxed + // Cross-Origin Read Blocking enforcement as needed by some extensions). + // + // If the embedder doesn't want to override the URLLoaderFactory for the given + // |request_initiator|, then it should return an invalid + // mojo::InterfacePtrInfo. + virtual network::mojom::URLLoaderFactoryPtrInfo + CreateURLLoaderFactoryForNetworkRequests( + RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator); + // Returns a list additional WebUI schemes, if any. These additional schemes // act as aliases to the chrome: scheme. The additional schemes may or may // not serve specific WebUI pages depending on the particular URLDataSource @@ -1114,9 +1136,7 @@ NonNetworkURLLoaderFactoryMap* factories); // Allows the embedder to intercept URLLoaderFactory interfaces used for - // navigation or being brokered on behalf of a renderer fetching subresources, - // or for non-navigation requests initiated by the browser on behalf of a - // BrowserContext. + // navigation or being brokered on behalf of a renderer fetching subresources. // // |is_navigation| is true when it's a request used for navigation. // @@ -1126,9 +1146,7 @@ // it's a request for a renderer fetching subresources. It's not set when // creating a factory for navigation requests, because navigation requests are // made on behalf of the browser, rather than on behalf of any particular - // origin. It's not set in the case of browser-initiated, non-navigation - // requests, because in that case the factory is cached and it can be used for - // multiple URLs. + // origin. // // |*factory_request| is always valid upon entry and MUST be valid upon // return. The embedder may swap out the value of |*factory_request| for its @@ -1141,10 +1159,6 @@ // // Always called on the UI thread and only when the Network Service is // enabled. - // - // Note that |frame| may be null if this is a browser-initiated, - // non-navigation request, e.g. a request made via - // |StoragePartition::GetURLLoaderFactoryForBrowserProcess()|. virtual bool WillCreateURLLoaderFactory( BrowserContext* browser_context, RenderFrameHost* frame,
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h index b819f86..b886735a 100644 --- a/content/public/browser/media_session.h +++ b/content/public/browser/media_session.h
@@ -8,7 +8,7 @@ #include "base/macros.h" #include "base/time/time.h" #include "content/common/content_export.h" -#include "services/media_session/public/mojom/audio_focus.mojom.h" +#include "services/media_session/public/mojom/media_session.mojom.h" namespace blink { namespace mojom { @@ -28,15 +28,6 @@ // and allows clients to resume/suspend/stop the managed players. class MediaSession : public media_session::mojom::MediaSession { public: - enum class SuspendType { - // Suspended by the system because a transient sound needs to be played. - kSystem, - // Suspended by the UI. - kUI, - // Suspended by the page via script or user interaction. - kContent, - }; - // Returns the MediaSession associated to this WebContents. Creates one if // none is currently available. CONTENT_EXPORT static MediaSession* Get(WebContents* contents); @@ -47,10 +38,6 @@ // |type| represents the origin of the request. virtual void Resume(SuspendType suspend_type) = 0; - // Suspend the media session. - // |type| represents the origin of the request. - virtual void Suspend(SuspendType suspend_type) = 0; - // Stop the media session. // |type| represents the origin of the request. virtual void Stop(SuspendType suspend_type) = 0; @@ -74,13 +61,29 @@ // Set the volume multiplier applied during ducking. virtual void SetDuckingVolumeMultiplier(double multiplier) = 0; + // media_session.mojom.MediaSession overrides ------------------------------- + + // Suspend the media session. + // |type| represents the origin of the request. + void Suspend(SuspendType suspend_type) override = 0; + // Let the media session start ducking such that the volume multiplier is // reduced. - virtual void StartDucking() = 0; + void StartDucking() override = 0; // Let the media session stop ducking such that the volume multiplier is // recovered. - virtual void StopDucking() = 0; + void StopDucking() override = 0; + + // Returns information about the MediaSession. + void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override = 0; + + // Returns debug information about the MediaSession. + void GetDebugInfo(GetDebugInfoCallback callback) override = 0; + + // Adds an observer to listen to events related to this MediaSession. + void AddObserver( + media_session::mojom::MediaSessionObserverPtr observer) override = 0; protected: MediaSession() = default;
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h index 76279d8..ce6a283 100644 --- a/content/public/browser/render_frame_host.h +++ b/content/public/browser/render_frame_host.h
@@ -321,6 +321,15 @@ virtual bool CreateNetworkServiceDefaultFactory( network::mojom::URLLoaderFactoryRequest default_factory_request) = 0; + // Requests that future URLLoaderFactoryBundle(s) sent to the renderer should + // use a separate URLLoaderFactory for requests initiated by any of the + // origins listed in |request_initiators|. The URLLoaderFactory(s) for each + // origin will be created via + // ContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests method. + virtual void MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + std::vector<url::Origin> request_initiators, + bool push_to_renderer_now) = 0; + private: // This interface should only be implemented inside content. friend class RenderFrameHostImpl;
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h index ef2a042..12d380a 100644 --- a/content/public/browser/render_process_host.h +++ b/content/public/browser/render_process_host.h
@@ -396,7 +396,9 @@ virtual resource_coordinator::ProcessResourceCoordinator* GetProcessResourceCoordinator() = 0; - // Create an URLLoaderFactory for this process. + // Create an URLLoaderFactory that can be used by |origin| being hosted in + // |this| process. + // // When NetworkService is enabled, |request| will be bound with a new // URLLoaderFactory created from the storage partition's Network Context. Note // that the URLLoaderFactory returned by this method does NOT support @@ -404,6 +406,7 @@ // When NetworkService is not enabled, |request| will be bound with a // URLLoaderFactory which routes requests to ResourceDispatcherHost. virtual void CreateURLLoaderFactory( + const url::Origin& origin, network::mojom::URLLoaderFactoryRequest request) = 0; // Whether this process is locked out from ever being reused for sites other
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h index 514b39f..7ccb8771 100644 --- a/content/public/browser/render_widget_host.h +++ b/content/public/browser/render_widget_host.h
@@ -165,6 +165,10 @@ virtual void Focus() = 0; virtual void Blur() = 0; + // Tests may need to flush IPCs to ensure deterministic behavior. + using FlushForTestingCallback = base::OnceClosure; + virtual void FlushForTesting(FlushForTestingCallback callback) = 0; + // Sets whether the renderer should show controls in an active state. On all // platforms except mac, that's the same as focused. On mac, the frontmost // window will show active controls even if the focus is not in the web
diff --git a/content/public/browser/site_isolation_policy.cc b/content/public/browser/site_isolation_policy.cc index 834a5cc..5d1b90a0 100644 --- a/content/public/browser/site_isolation_policy.cc +++ b/content/public/browser/site_isolation_policy.cc
@@ -59,13 +59,6 @@ params->is_corb_enabled = true; params->corb_detachable_resource_type = RESOURCE_TYPE_PREFETCH; params->corb_excluded_resource_type = RESOURCE_TYPE_PLUGIN_RESOURCE; - - const char* initiator_scheme_exception = - GetContentClient() - ->browser() - ->GetInitiatorSchemeBypassingDocumentBlocking(); - if (initiator_scheme_exception) - params->corb_excluded_initiator_scheme = initiator_scheme_exception; } // static
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc index 3bc31a64..86f3de9 100644 --- a/content/public/test/mock_render_process_host.cc +++ b/content/public/test/mock_render_process_host.cc
@@ -399,6 +399,7 @@ } void MockRenderProcessHost::CreateURLLoaderFactory( + const url::Origin& origin, network::mojom::URLLoaderFactoryRequest request) { url_loader_factory_->Clone(std::move(request)); }
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h index ca37e77e..92596ac 100644 --- a/content/public/test/mock_render_process_host.h +++ b/content/public/test/mock_render_process_host.h
@@ -136,6 +136,7 @@ resource_coordinator::ProcessResourceCoordinator* GetProcessResourceCoordinator() override; void CreateURLLoaderFactory( + const url::Origin& origin, network::mojom::URLLoaderFactoryRequest request) override; void SetIsNeverSuitableForReuse() override;
diff --git a/content/renderer/input/widget_input_handler_impl.cc b/content/renderer/input/widget_input_handler_impl.cc index f50fe69d..ee9924f 100644 --- a/content/renderer/input/widget_input_handler_impl.cc +++ b/content/renderer/input/widget_input_handler_impl.cc
@@ -67,6 +67,18 @@ base::BindOnce(&WidgetInputHandlerImpl::Release, base::Unretained(this))); } +void WidgetInputHandlerImpl::FlushForTesting(FlushForTestingCallback callback) { + auto run_on_this_thread = base::BindOnce( + [](scoped_refptr<base::SingleThreadTaskRunner> task_runner, + FlushForTestingCallback callback) { + task_runner->PostTask(FROM_HERE, std::move(callback)); + }, + base::ThreadTaskRunnerHandle::Get(), std::move(callback)); + + // Hop to main thread to ensure everything there is flushed. + RunOnMainThread(std::move(run_on_this_thread)); +} + void WidgetInputHandlerImpl::SetFocus(bool focused) { RunOnMainThread( base::BindOnce(&RenderWidget::OnSetFocus, render_widget_, focused));
diff --git a/content/renderer/input/widget_input_handler_impl.h b/content/renderer/input/widget_input_handler_impl.h index 689f973a..704742d 100644 --- a/content/renderer/input/widget_input_handler_impl.h +++ b/content/renderer/input/widget_input_handler_impl.h
@@ -32,6 +32,7 @@ mojom::WidgetInputHandlerAssociatedRequest interface_request); void SetBinding(mojom::WidgetInputHandlerRequest interface_request); + void FlushForTesting(FlushForTestingCallback callback) override; void SetFocus(bool focused) override; void MouseCaptureLost() override; void SetEditCommandsForNextKeyEvent(
diff --git a/content/renderer/loader/child_url_loader_factory_bundle.cc b/content/renderer/loader/child_url_loader_factory_bundle.cc index d000662..c7f2695 100644 --- a/content/renderer/loader/child_url_loader_factory_bundle.cc +++ b/content/renderer/loader/child_url_loader_factory_bundle.cc
@@ -94,6 +94,19 @@ network::mojom::URLLoaderClientPtr client_sink_; }; +template <typename TKey> +static std::map<TKey, network::mojom::URLLoaderFactoryPtrInfo> +PassInterfacePtrMapToPtrInfoMap( + std::map<TKey, network::mojom::URLLoaderFactoryPtr> input) { + std::map<TKey, network::mojom::URLLoaderFactoryPtrInfo> output; + for (auto& it : input) { + const TKey& key = it.first; + network::mojom::URLLoaderFactoryPtr& factory = it.second; + output.emplace(key, factory.PassInterface()); + } + return output; +} + } // namespace ChildURLLoaderFactoryBundleInfo::ChildURLLoaderFactoryBundleInfo() = default; @@ -103,15 +116,18 @@ : URLLoaderFactoryBundleInfo( std::move(base_info->default_factory_info()), std::move(base_info->scheme_specific_factory_infos()), + std::move(base_info->initiator_specific_factory_infos()), base_info->bypass_redirect_checks()) {} ChildURLLoaderFactoryBundleInfo::ChildURLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, PossiblyAssociatedURLLoaderFactoryPtrInfo direct_network_factory_info, bool bypass_redirect_checks) : URLLoaderFactoryBundleInfo(std::move(default_factory_info), std::move(scheme_specific_factory_infos), + std::move(initiator_specific_factory_infos), bypass_redirect_checks), direct_network_factory_info_(std::move(direct_network_factory_info)) {} @@ -123,6 +139,8 @@ other->default_factory_info_ = std::move(default_factory_info_); other->scheme_specific_factory_infos_ = std::move(scheme_specific_factory_infos_); + other->initiator_specific_factory_infos_ = + std::move(initiator_specific_factory_infos_); other->direct_network_factory_info_ = std::move(direct_network_factory_info_); other->bypass_redirect_checks_ = bypass_redirect_checks_; @@ -145,10 +163,10 @@ ChildURLLoaderFactoryBundle::~ChildURLLoaderFactoryBundle() = default; -network::mojom::URLLoaderFactory* ChildURLLoaderFactoryBundle::GetFactoryForURL( - const GURL& url) { +network::mojom::URLLoaderFactory* ChildURLLoaderFactoryBundle::GetFactory( + const network::ResourceRequest& request) { network::mojom::URLLoaderFactory* base_result = - URLLoaderFactoryBundle::GetFactoryForURL(url); + URLLoaderFactoryBundle::GetFactory(request); if (base_result) return base_result; @@ -238,14 +256,6 @@ if (include_default && default_factory_) default_factory_->Clone(mojo::MakeRequest(&default_factory_info)); - URLLoaderFactoryBundleInfo::SchemeMap scheme_specific_factory_infos; - for (auto& factory : scheme_specific_factories_) { - network::mojom::URLLoaderFactoryPtrInfo factory_info; - factory.second->Clone(mojo::MakeRequest(&factory_info)); - scheme_specific_factory_infos.emplace(factory.first, - std::move(factory_info)); - } - network::mojom::URLLoaderFactoryPtrInfo direct_network_factory_info; if (direct_network_factory_) { direct_network_factory_->Clone( @@ -256,7 +266,9 @@ // therefore |subresource_overrides| are not shared with the clones. return std::make_unique<ChildURLLoaderFactoryBundleInfo>( - std::move(default_factory_info), std::move(scheme_specific_factory_infos), + std::move(default_factory_info), + ClonePtrMapToPtrInfoMap(scheme_specific_factories_), + ClonePtrMapToPtrInfoMap(initiator_specific_factories_), std::move(direct_network_factory_info), bypass_redirect_checks_); } @@ -268,12 +280,6 @@ if (default_factory_) default_factory_info = default_factory_.PassInterface(); - URLLoaderFactoryBundleInfo::SchemeMap scheme_specific_factory_infos; - for (auto& factory : scheme_specific_factories_) { - scheme_specific_factory_infos.emplace(factory.first, - factory.second.PassInterface()); - } - PossiblyAssociatedInterfacePtrInfo<network::mojom::URLLoaderFactory> direct_network_factory_info; if (direct_network_factory_) { @@ -281,7 +287,9 @@ } return std::make_unique<ChildURLLoaderFactoryBundleInfo>( - std::move(default_factory_info), std::move(scheme_specific_factory_infos), + std::move(default_factory_info), + PassInterfacePtrMapToPtrInfoMap(std::move(scheme_specific_factories_)), + PassInterfacePtrMapToPtrInfoMap(std::move(initiator_specific_factories_)), std::move(direct_network_factory_info), bypass_redirect_checks_); }
diff --git a/content/renderer/loader/child_url_loader_factory_bundle.h b/content/renderer/loader/child_url_loader_factory_bundle.h index 05c4fbda..333518c 100644 --- a/content/renderer/loader/child_url_loader_factory_bundle.h +++ b/content/renderer/loader/child_url_loader_factory_bundle.h
@@ -33,6 +33,7 @@ ChildURLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, PossiblyAssociatedURLLoaderFactoryPtrInfo direct_network_factory_info, bool bypass_redirect_checks); ~ChildURLLoaderFactoryBundleInfo() override; @@ -101,7 +102,8 @@ ~ChildURLLoaderFactoryBundle() override; // URLLoaderFactoryBundle overrides. - network::mojom::URLLoaderFactory* GetFactoryForURL(const GURL& url) override; + network::mojom::URLLoaderFactory* GetFactory( + const network::ResourceRequest& request) override; private: void InitDirectNetworkFactoryIfNecessary();
diff --git a/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc b/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc index edbd4d48..d51228c 100644 --- a/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc +++ b/content/renderer/loader/tracked_child_url_loader_factory_bundle.cc
@@ -16,13 +16,16 @@ TrackedChildURLLoaderFactoryBundleInfo::TrackedChildURLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, PossiblyAssociatedURLLoaderFactoryPtrInfo direct_network_factory_info, std::unique_ptr<HostPtrAndTaskRunner> main_thread_host_bundle, bool bypass_redirect_checks) - : ChildURLLoaderFactoryBundleInfo(std::move(default_factory_info), - std::move(scheme_specific_factory_infos), - std::move(direct_network_factory_info), - bypass_redirect_checks), + : ChildURLLoaderFactoryBundleInfo( + std::move(default_factory_info), + std::move(scheme_specific_factory_infos), + std::move(initiator_specific_factory_infos), + std::move(direct_network_factory_info), + bypass_redirect_checks), main_thread_host_bundle_(std::move(main_thread_host_bundle)) {} TrackedChildURLLoaderFactoryBundleInfo:: @@ -34,6 +37,8 @@ other->default_factory_info_ = std::move(default_factory_info_); other->scheme_specific_factory_infos_ = std::move(scheme_specific_factory_infos_); + other->initiator_specific_factory_infos_ = + std::move(initiator_specific_factory_infos_); other->direct_network_factory_info_ = std::move(direct_network_factory_info_); other->main_thread_host_bundle_ = std::move(main_thread_host_bundle_); other->bypass_redirect_checks_ = bypass_redirect_checks_; @@ -69,6 +74,7 @@ return std::make_unique<TrackedChildURLLoaderFactoryBundleInfo>( std::move(info->default_factory_info()), std::move(info->scheme_specific_factory_infos()), + std::move(info->initiator_specific_factory_infos()), std::move(info->direct_network_factory_info()), std::move(main_thread_host_bundle_clone), info->bypass_redirect_checks()); } @@ -132,6 +138,7 @@ return std::make_unique<TrackedChildURLLoaderFactoryBundleInfo>( std::move(info->default_factory_info()), std::move(info->scheme_specific_factory_infos()), + std::move(info->initiator_specific_factory_infos()), std::move(info->direct_network_factory_info()), std::move(main_thread_host_bundle_clone), info->bypass_redirect_checks()); } @@ -149,6 +156,7 @@ return std::make_unique<TrackedChildURLLoaderFactoryBundleInfo>( std::move(info->default_factory_info()), std::move(info->scheme_specific_factory_infos()), + std::move(info->initiator_specific_factory_infos()), std::move(info->direct_network_factory_info()), std::move(main_thread_host_bundle_clone), info->bypass_redirect_checks()); }
diff --git a/content/renderer/loader/tracked_child_url_loader_factory_bundle.h b/content/renderer/loader/tracked_child_url_loader_factory_bundle.h index b52d322..7348be1 100644 --- a/content/renderer/loader/tracked_child_url_loader_factory_bundle.h +++ b/content/renderer/loader/tracked_child_url_loader_factory_bundle.h
@@ -30,6 +30,7 @@ TrackedChildURLLoaderFactoryBundleInfo( network::mojom::URLLoaderFactoryPtrInfo default_factory_info, SchemeMap scheme_specific_factory_infos, + OriginMap initiator_specific_factory_infos, PossiblyAssociatedURLLoaderFactoryPtrInfo direct_network_factory_info, std::unique_ptr<HostPtrAndTaskRunner> main_thread_host_bundle, bool bypass_redirect_checks);
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc index a3869e8..a87a956 100644 --- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc +++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -20,6 +20,7 @@ #include "media/base/video_util.h" #include "third_party/webrtc/api/video/i420_buffer.h" #include "third_party/webrtc/api/video/video_sink_interface.h" +#include "third_party/webrtc/rtc_base/timeutils.h" // for TimeMicros namespace content {
diff --git a/content/shell/browser/layout_test/blink_test_controller.cc b/content/shell/browser/layout_test/blink_test_controller.cc index f35a2bec..add1727 100644 --- a/content/shell/browser/layout_test/blink_test_controller.cc +++ b/content/shell/browser/layout_test/blink_test_controller.cc
@@ -408,6 +408,14 @@ if (is_devtools_js_test) { LoadDevToolsJSTest(); } else { + // Flush IPC messages on the widget. + base::RunLoop run_loop; + main_window_->web_contents() + ->GetRenderViewHost() + ->GetWidget() + ->FlushForTesting(run_loop.QuitClosure()); + run_loop.Run(); + // Loading the URL will immediately start the layout test. Manually call // LoadURLWithParams on the WebContents to avoid extraneous calls from // content::Shell such as SetFocus(), which could race with the layout
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 8a6d59cb..313dbfb 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -47,6 +47,8 @@ "../browser/background_fetch/background_fetch_test_data_manager.h", "../browser/background_fetch/mock_background_fetch_delegate.cc", "../browser/background_fetch/mock_background_fetch_delegate.h", + "../browser/media/session/audio_focus_test_util.cc", + "../browser/media/session/audio_focus_test_util.h", "../browser/media/session/mock_media_session_observer.cc", "../browser/media/session/mock_media_session_observer.h", "../browser/service_worker/embedded_worker_test_helper.cc", @@ -1460,6 +1462,7 @@ "../browser/media/session/media_session_controllers_manager_unittest.cc", "../browser/media/session/media_session_impl_service_routing_unittest.cc", "../browser/media/session/media_session_impl_uma_unittest.cc", + "../browser/media/session/media_session_impl_unittest.cc", "../browser/media/session/media_session_uma_helper_unittest.cc", "../browser/memory/memory_coordinator_impl_unittest.cc", "../browser/memory/memory_monitor_android_unittest.cc",
diff --git a/content/test/mock_widget_input_handler.cc b/content/test/mock_widget_input_handler.cc index 09fc3b1f9..71f96b1 100644 --- a/content/test/mock_widget_input_handler.cc +++ b/content/test/mock_widget_input_handler.cc
@@ -26,6 +26,10 @@ MockWidgetInputHandler::~MockWidgetInputHandler() {} +void MockWidgetInputHandler::FlushForTesting(FlushForTestingCallback callback) { + std::move(callback).Run(); +} + void MockWidgetInputHandler::SetFocus(bool focused) { dispatched_messages_.emplace_back( std::make_unique<DispatchedFocusMessage>(focused));
diff --git a/content/test/mock_widget_input_handler.h b/content/test/mock_widget_input_handler.h index 4e87ef92..f886125 100644 --- a/content/test/mock_widget_input_handler.h +++ b/content/test/mock_widget_input_handler.h
@@ -192,6 +192,7 @@ }; // mojom::WidgetInputHandler override. + void FlushForTesting(FlushForTestingCallback callback) override; void SetFocus(bool focused) override; void MouseCaptureLost() override; void SetEditCommandsForNextKeyEvent(
diff --git a/docs/security/faq.md b/docs/security/faq.md index 89c21628..218d66dd 100644 --- a/docs/security/faq.md +++ b/docs/security/faq.md
@@ -157,8 +157,14 @@ as that forces us to embargo the information in the bug. Note that the XSSAuditor is not able to defend against persistent XSS or -DOM-based XSS. There will also be a number of infrequently occurring reflected -XSS corner cases that it will never be able to cover. Among these are: +DOM-based XSS. Nor is it able to defend against injections deep inside +existing JavaScript blocks, [for +example](https://bugs.chromium.org/p/chromium/issues/detail?id=135029), since +the XSSAuditor is part of the HTML parser, not the JavaScript parser. + +There will also be a number of infrequently occurring reflected XSS corner +case in an HTML context that it will never be able to cover. Among +these are: * Multiple unsanitized variables injected into the page. * Unexpected server side transformation or decoding of the payload.
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index c548631..6c1177c 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn
@@ -337,6 +337,8 @@ "uninstall_ping_sender.h", "uninstall_reason.h", "update_observer.h", + "url_loader_factory_manager.cc", + "url_loader_factory_manager.h", "url_request_util.cc", "url_request_util.h", "user_script_loader.cc",
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn index b4fb58b..1adcdad 100644 --- a/extensions/browser/api/BUILD.gn +++ b/extensions/browser/api/BUILD.gn
@@ -145,6 +145,7 @@ "//chromeos", "//chromeos:media_perception_proto", "//chromeos/services/media_perception/public/mojom", + "//chromeos/services/media_perception/public/mojom:mojom_js_data_deps", ] } }
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_delegate.h b/extensions/browser/api/media_perception_private/media_perception_api_delegate.h index 6692365e..56e789f 100644 --- a/extensions/browser/api/media_perception_private/media_perception_api_delegate.h +++ b/extensions/browser/api/media_perception_private/media_perception_api_delegate.h
@@ -5,12 +5,21 @@ #ifndef EXTENSIONS_BROWSER_API_MEDIA_PERCEPTION_PRIVATE_MEDIA_PERCEPTION_API_DELEGATE_H_ #define EXTENSIONS_BROWSER_API_MEDIA_PERCEPTION_PRIVATE_MEDIA_PERCEPTION_API_DELEGATE_H_ +#include <memory> + #include "base/callback.h" #include "base/files/file_path.h" +#include "chromeos/services/media_perception/public/mojom/media_perception_service.mojom.h" #include "extensions/common/api/media_perception_private.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +namespace content { + +class RenderFrameHost; + +} // namespace content + namespace extensions { class MediaPerceptionAPIDelegate { @@ -20,6 +29,9 @@ using LoadCrOSComponentCallback = base::OnceCallback<void(bool success, const base::FilePath& mount_point)>; + using MediaPerceptionRequestHandler = base::RepeatingCallback<void( + chromeos::media_perception::mojom::MediaPerceptionRequest request)>; + virtual ~MediaPerceptionAPIDelegate() {} // Provides an interface through which a media analytics Chrome OS component @@ -34,6 +46,17 @@ // |provider| is owned by the caller. virtual void BindDeviceFactoryProviderToVideoCaptureService( video_capture::mojom::DeviceFactoryProviderPtr* provider) = 0; + + // Provides an interface to set a handler for an incoming + // MediaPerceptionRequest. + virtual void SetMediaPerceptionRequestHandler( + MediaPerceptionRequestHandler handler) = 0; + + // Receives an incoming media perception request and forwards it to the + // request handler if set. + virtual void ForwardMediaPerceptionRequest( + chromeos::media_perception::mojom::MediaPerceptionRequest request, + content::RenderFrameHost* render_frame_host) = 0; }; } // namespace extensions
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc index 62230df..fd9ca85a 100644 --- a/extensions/browser/api/media_perception_private/media_perception_api_manager.cc +++ b/extensions/browser/api/media_perception_private/media_perception_api_manager.cc
@@ -18,41 +18,16 @@ #include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_function.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/invitation.h" +#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace extensions { namespace { -class ConnectorImpl : public chromeos::media_perception::mojom::Connector { - public: - // delegate is owned by the ExtensionsAPIClient. - explicit ConnectorImpl(MediaPerceptionAPIDelegate* delegate) - : delegate_(delegate) {} - ~ConnectorImpl() override = default; - - // media_perception::mojom::Connector override. - void ConnectToVideoCaptureService( - video_capture::mojom::DeviceFactoryRequest request) override { - DCHECK(delegate_) << "Delegate not set."; - delegate_->BindDeviceFactoryProviderToVideoCaptureService( - &device_factory_provider_); - device_factory_provider_->ConnectToDeviceFactory(std::move(request)); - } - - private: - // Provides access to methods for talking to core Chrome code. - MediaPerceptionAPIDelegate* delegate_; - - // Bound to the VideoCaptureService to establish the connection to the - // media analytics process. - video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_; - - DISALLOW_COPY_AND_ASSIGN(ConnectorImpl); -}; - extensions::api::media_perception_private::State GetStateForServiceError( const extensions::api::media_perception_private::ServiceError service_error) { @@ -102,6 +77,46 @@ } // namespace +class MediaPerceptionAPIManager::MediaPerceptionControllerClient + : public chromeos::media_perception::mojom:: + MediaPerceptionControllerClient { + public: + // delegate is owned by the ExtensionsAPIClient. + MediaPerceptionControllerClient( + MediaPerceptionAPIDelegate* delegate, + chromeos::media_perception::mojom::MediaPerceptionControllerClientRequest + request) + : delegate_(delegate), binding_(this, std::move(request)) { + DCHECK(delegate_) << "Delegate not set."; + } + + ~MediaPerceptionControllerClient() override = default; + + // media_perception::mojom::MediaPerceptionControllerClient: + void ConnectToVideoCaptureService( + video_capture::mojom::DeviceFactoryRequest request) override { + DCHECK(delegate_) << "Delegate not set."; + delegate_->BindDeviceFactoryProviderToVideoCaptureService( + &device_factory_provider_); + device_factory_provider_->ConnectToDeviceFactory(std::move(request)); + } + + private: + // Provides access to methods for talking to core Chrome code. + MediaPerceptionAPIDelegate* delegate_; + + // Binding of the MediaPerceptionControllerClient to the message pipe. + mojo::Binding< + chromeos::media_perception::mojom::MediaPerceptionControllerClient> + binding_; + + // Bound to the VideoCaptureService to establish the connection to the + // media analytics process. + video_capture::mojom::DeviceFactoryProviderPtr device_factory_provider_; + + DISALLOW_COPY_AND_ASSIGN(MediaPerceptionControllerClient); +}; + // static MediaPerceptionAPIManager* MediaPerceptionAPIManager::Get( content::BrowserContext* context) { @@ -135,6 +150,12 @@ upstart_client->StopMediaAnalytics(); } +void MediaPerceptionAPIManager::ActivateMediaPerception( + chromeos::media_perception::mojom::MediaPerceptionRequest request) { + if (media_perception_controller_.is_bound()) + media_perception_controller_->ActivateMediaPerception(std::move(request)); +} + void MediaPerceptionAPIManager::SetMountPointNonEmptyForTesting() { mount_point_ = "non-empty-string"; } @@ -390,7 +411,7 @@ MediaPerceptionAPIDelegate* delegate = ExtensionsAPIClient::Get()->GetMediaPerceptionAPIDelegate(); if (!delegate) { - LOG(WARNING) << "Could not get MediaPerceptionAPIDelegate."; + DLOG(WARNING) << "Could not get MediaPerceptionAPIDelegate."; std::move(callback).Run(GetProcessStateForServiceError( extensions::api::media_perception_private:: SERVICE_ERROR_MOJO_CONNECTION_FAILURE)); @@ -405,10 +426,10 @@ base::kNullProcessHandle, channel.TakeLocalEndpoint()); - auto connector = std::make_unique<ConnectorImpl>(delegate); - mojo::MakeStrongBinding(std::move(connector), - chromeos::media_perception::mojom::ConnectorRequest( - std::move(server_pipe))); + media_perception_service_ = + chromeos::media_perception::mojom::MediaPerceptionServicePtr( + chromeos::media_perception::mojom::MediaPerceptionServicePtrInfo( + std::move(server_pipe), 0)); base::ScopedFD fd = channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD(); @@ -435,6 +456,46 @@ extensions::api::media_perception_private::ProcessState state_started; state_started.status = extensions::api::media_perception_private::PROCESS_STATUS_STARTED; + + // Check if the extensions api client is available in this context. Code path + // used for testing. + if (!ExtensionsAPIClient::Get()) { + DLOG(ERROR) << "Could not get ExtensionsAPIClient."; + std::move(callback).Run(std::move(state_started)); + return; + } + + MediaPerceptionAPIDelegate* delegate = + ExtensionsAPIClient::Get()->GetMediaPerceptionAPIDelegate(); + if (!delegate) { + DLOG(WARNING) << "Could not get MediaPerceptionAPIDelegate."; + std::move(callback).Run(GetProcessStateForServiceError( + extensions::api::media_perception_private:: + SERVICE_ERROR_MOJO_CONNECTION_FAILURE)); + return; + } + + if (!media_perception_service_.is_bound()) { + DLOG(WARNING) << "MediaPerceptionService interface not bound."; + std::move(callback).Run(GetProcessStateForServiceError( + extensions::api::media_perception_private:: + SERVICE_ERROR_MOJO_CONNECTION_FAILURE)); + return; + } + + auto controller_request = mojo::MakeRequest(&media_perception_controller_); + + chromeos::media_perception::mojom::MediaPerceptionControllerClientPtr + client_ptr; + media_perception_controller_client_ = + std::make_unique<MediaPerceptionControllerClient>( + delegate, mojo::MakeRequest(&client_ptr)); + delegate->SetMediaPerceptionRequestHandler( + base::BindRepeating(&MediaPerceptionAPIManager::ActivateMediaPerception, + weak_ptr_factory_.GetWeakPtr())); + + media_perception_service_->GetController(std::move(controller_request), + std::move(client_ptr)); std::move(callback).Run(std::move(state_started)); }
diff --git a/extensions/browser/api/media_perception_private/media_perception_api_manager.h b/extensions/browser/api/media_perception_private/media_perception_api_manager.h index 45b63d2..4f38e6f 100644 --- a/extensions/browser/api/media_perception_private/media_perception_api_manager.h +++ b/extensions/browser/api/media_perception_private/media_perception_api_manager.h
@@ -12,10 +12,9 @@ #include "base/scoped_observer.h" #include "chromeos/dbus/media_analytics_client.h" #include "chromeos/dbus/media_perception/media_perception.pb.h" -#include "chromeos/services/media_perception/public/mojom/connector.mojom.h" +#include "chromeos/services/media_perception/public/mojom/media_perception_service.mojom.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/common/api/media_perception_private.h" -#include "services/video_capture/public/mojom/device_factory_provider.mojom.h" namespace extensions { @@ -47,6 +46,10 @@ static BrowserContextKeyedAPIFactory<MediaPerceptionAPIManager>* GetFactoryInstance(); + // Handler for clients of the API requesting a MediaPerception Mojo interface. + void ActivateMediaPerception( + chromeos::media_perception::mojom::MediaPerceptionRequest request); + // Public functions for MediaPerceptionPrivateAPI implementation. void SetAnalyticsComponent( const extensions::api::media_perception_private::Component& component, @@ -67,6 +70,8 @@ private: friend class BrowserContextKeyedAPIFactory<MediaPerceptionAPIManager>; + class MediaPerceptionControllerClient; + // BrowserContextKeyedAPI: static const char* service_name() { return "MediaPerceptionAPIManager"; } @@ -142,6 +147,17 @@ // is set. std::string mount_point_; + // Pointer to the MediaPerceptionService interface for communicating with the + // service over Mojo. + chromeos::media_perception::mojom::MediaPerceptionServicePtr + media_perception_service_; + + chromeos::media_perception::mojom::MediaPerceptionControllerPtr + media_perception_controller_; + + std::unique_ptr<MediaPerceptionControllerClient> + media_perception_controller_client_; + ScopedObserver<chromeos::MediaAnalyticsClient, MediaPerceptionAPIManager> scoped_observer_; base::WeakPtrFactory<MediaPerceptionAPIManager> weak_ptr_factory_;
diff --git a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc index 1c83e66..305b1a0 100644 --- a/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc +++ b/extensions/browser/api/media_perception_private/media_perception_private_apitest.cc
@@ -49,7 +49,18 @@ void BindDeviceFactoryProviderToVideoCaptureService( video_capture::mojom::DeviceFactoryProviderPtr* provider) override { - LOG(ERROR) << "Not implemented."; + NOTIMPLEMENTED(); + } + + void SetMediaPerceptionRequestHandler( + MediaPerceptionRequestHandler handler) override { + NOTIMPLEMENTED(); + } + + void ForwardMediaPerceptionRequest( + chromeos::media_perception::mojom::MediaPerceptionRequest request, + content::RenderFrameHost* render_frame_host) override { + NOTIMPLEMENTED(); } };
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc index 5202159..b005d50 100644 --- a/extensions/browser/api/web_request/web_request_api.cc +++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -600,20 +600,18 @@ bool skip_proxy = true; // There are a few internal WebUIs that use WebView tag that are whitelisted // for webRequest. - if (frame) { - auto* web_contents = content::WebContents::FromRenderFrameHost(frame); - if (web_contents && WebViewGuest::IsGuest(web_contents)) { - auto* guest_web_contents = - WebViewGuest::GetTopLevelWebContents(web_contents); - auto& guest_url = guest_web_contents->GetURL(); - if (guest_url.SchemeIs(content::kChromeUIScheme)) { - auto* feature = FeatureProvider::GetAPIFeature("webRequestInternal"); - if (feature - ->IsAvailableToContext(nullptr, Feature::WEBUI_CONTEXT, - guest_url) - .is_available()) { - skip_proxy = false; - } + auto* web_contents = content::WebContents::FromRenderFrameHost(frame); + if (web_contents && WebViewGuest::IsGuest(web_contents)) { + auto* guest_web_contents = + WebViewGuest::GetTopLevelWebContents(web_contents); + auto& guest_url = guest_web_contents->GetURL(); + if (guest_url.SchemeIs(content::kChromeUIScheme)) { + auto* feature = FeatureProvider::GetAPIFeature("webRequestInternal"); + if (feature + ->IsAvailableToContext(nullptr, Feature::WEBUI_CONTEXT, + guest_url) + .is_available()) { + skip_proxy = false; } } } @@ -639,25 +637,22 @@ // NOTE: This request may be proxied on behalf of an incognito frame, but // |this| will always be bound to a regular profile (see // |BrowserContextKeyedAPI::kServiceRedirectedInIncognito|). As such, we use - // the frame's BrowserContext when |frame| is non-null. - auto* browser_context = - frame ? frame->GetProcess()->GetBrowserContext() : browser_context_; + // the frame's BrowserContext. + auto* browser_context = frame->GetProcess()->GetBrowserContext(); DCHECK(browser_context == browser_context_ || (browser_context->IsOffTheRecord() && ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context) == browser_context_)); - const bool is_for_browser_initiated_requests = is_navigation || !frame; base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, - base::BindOnce( - &WebRequestProxyingURLLoaderFactory::StartProxying, browser_context, - browser_context->GetResourceContext(), - // Match the behavior of the WebRequestInfo constructor - // which takes a net::URLRequest*. - is_for_browser_initiated_requests ? -1 : frame->GetProcess()->GetID(), - request_id_generator_, std::move(navigation_ui_data), - base::Unretained(info_map_), std::move(proxied_request), - std::move(target_factory_info))); + base::BindOnce(&WebRequestProxyingURLLoaderFactory::StartProxying, + browser_context, browser_context->GetResourceContext(), + // Match the behavior of the WebRequestInfo constructor + // which takes a net::URLRequest*. + is_navigation ? -1 : frame->GetProcess()->GetID(), + request_id_generator_, std::move(navigation_ui_data), + base::Unretained(info_map_), std::move(proxied_request), + std::move(target_factory_info))); return true; }
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc index bfae9c4..2ea6f265 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -25,7 +25,6 @@ int32_t network_service_request_id, int32_t routing_id, uint32_t options, - bool is_non_navigation_browser_request, const network::ResourceRequest& request, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, network::mojom::URLLoaderRequest loader_request, @@ -36,7 +35,6 @@ network_service_request_id_(network_service_request_id), routing_id_(routing_id), options_(options), - is_non_navigation_browser_request_(is_non_navigation_browser_request), traffic_annotation_(traffic_annotation), proxied_loader_binding_(this, std::move(loader_request)), target_client_(std::move(client)), @@ -69,22 +67,6 @@ routing_id_, factory_->resource_context_, request_, !(options_ & network::mojom::kURLLoadOptionSynchronous)); - if (is_non_navigation_browser_request_) { - // ResourceRequest always has a valid-looking ResourceType value since it's - // non-optional and defaults to 0 (i.e. MAIN_FRAME), even of the - // corresponding request didn't actually come from a renderer. Because - // |info_| was blindly constructed from that ResourceRequest, it also now - // appears to pertain to a main-frame request. - // - // Because we already know this is a browser-originated request, we - // explicitly reset |info_->type| to null. A request having no ResourceType - // effectively implies a browser-originated request to any subsequent - // WebRequest logic that cares, e.g. some permission checking to determine - // when to filter certain kinds of requests. - info_->type.reset(); - info_->web_request_type = WebRequestResourceType::OTHER; - } - auto continuation = base::BindRepeating(&InProgressRequest::ContinueToBeforeSendHeaders, weak_factory_.GetWeakPtr()); @@ -617,6 +599,9 @@ const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + // Make sure we are not proxying a browser initiated non-navigation request. + DCHECK(render_process_id_ != -1 || navigation_ui_data_); + // The request ID doesn't really matter in the Network Service path. It just // needs to be unique per-BrowserContext so extensions can make sense of it. // Note that |network_service_request_id_| by contrast is not necessarily @@ -633,18 +618,11 @@ network_request_id_to_web_request_id_.emplace(request_id, web_request_id); } - // The WebRequest API treats browser-originated non-navigation requests with a - // few additional restrictions, so we deduce and propagate that information - // here. - const bool is_non_navigation_browser_request = - render_process_id_ == -1 && !navigation_ui_data_; - auto result = requests_.emplace( web_request_id, std::make_unique<InProgressRequest>( - this, web_request_id, request_id, routing_id, options, - is_non_navigation_browser_request, request, traffic_annotation, - std::move(loader_request), std::move(client))); + this, web_request_id, request_id, routing_id, options, request, + traffic_annotation, std::move(loader_request), std::move(client))); result.first->second->Restart(); }
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h index 9d47f00..798b144 100644 --- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h +++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -55,7 +55,6 @@ int32_t routing_id, int32_t network_service_request_id, uint32_t options, - bool is_non_navigation_browser_request, const network::ResourceRequest& request, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, network::mojom::URLLoaderRequest loader_request, @@ -116,7 +115,6 @@ const int32_t network_service_request_id_; const int32_t routing_id_; const uint32_t options_; - const bool is_non_navigation_browser_request_; const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; mojo::Binding<network::mojom::URLLoader> proxied_loader_binding_; network::mojom::URLLoaderClientPtr target_client_;
diff --git a/extensions/browser/event_listener_map.h b/extensions/browser/event_listener_map.h index b571218..7ca17395 100644 --- a/extensions/browser/event_listener_map.h +++ b/extensions/browser/event_listener_map.h
@@ -144,6 +144,8 @@ class EventListenerMap { public: using ListenerList = std::vector<std::unique_ptr<EventListener>>; + // The key here is an event name. + using ListenerMap = std::unordered_map<std::string, ListenerList>; class Delegate { public: @@ -166,6 +168,9 @@ // Returns true if the listener was removed . bool RemoveListener(const EventListener* listener); + // Get the map of all EventListeners. + const ListenerMap& listeners() const { return listeners_; }; + // Returns the set of listeners that want to be notified of |event|. std::set<const EventListener*> GetEventListeners(const Event& event); @@ -218,8 +223,6 @@ const base::DictionaryValue& filtered); private: - // The key here is an event name. - using ListenerMap = std::map<std::string, ListenerList>; void CleanupListener(EventListener* listener); bool IsFilteredEvent(const Event& event) const;
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 2f194bc4..34e05501 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -1344,6 +1344,7 @@ AUTOTESTPRIVATE_BOOTSTRAPMACHINELEARNINGSERVICE = 1281, AUTOTESTPRIVATE_RUNCROSTINIUNINSTALLER = 1282, AUTOTESTPRIVATE_TAKESCREENSHOT = 1283, + ACCESSIBILITY_PRIVATE_TOGGLEDICTATION = 1284, // Last entry: Add new entries above, then run: // python tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc index 65c9e6a..6bfc6a5 100644 --- a/extensions/browser/extension_web_contents_observer.cc +++ b/extensions/browser/extension_web_contents_observer.cc
@@ -19,6 +19,7 @@ #include "extensions/browser/mojo/interface_registration.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/renderer_startup_helper.h" +#include "extensions/browser/url_loader_factory_manager.h" #include "extensions/browser/view_type_utils.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" @@ -172,6 +173,8 @@ void ExtensionWebContentsObserver::ReadyToCommitNavigation( content::NavigationHandle* navigation_handle) { + URLLoaderFactoryManager::ReadyToCommitNavigation(navigation_handle); + if (navigation_handle->IsInMainFrame() && !navigation_handle->IsSameDocument()) { ExtensionApiFrameIdMap::Get()->OnMainFrameReadyToCommitNavigation(
diff --git a/extensions/browser/script_executor.cc b/extensions/browser/script_executor.cc index a07e6bc..7364de2 100644 --- a/extensions/browser/script_executor.cc +++ b/extensions/browser/script_executor.cc
@@ -4,6 +4,9 @@ #include "extensions/browser/script_executor.h" +#include <set> +#include <string> + #include "base/bind.h" #include "base/callback.h" #include "base/hash.h" @@ -17,6 +20,7 @@ #include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/script_execution_observer.h" +#include "extensions/browser/url_loader_factory_manager.h" #include "extensions/common/extension_messages.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" @@ -125,6 +129,7 @@ if (!root_is_main_frame_ && !ShouldIncludeFrame(frame)) return; pending_render_frames_.insert(frame); + URLLoaderFactoryManager::WillExecuteCode(frame, host_id_); frame->Send(new ExtensionMsg_ExecuteCode(frame->GetRoutingID(), params)); }
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc new file mode 100644 index 0000000..7a94d5f --- /dev/null +++ b/extensions/browser/url_loader_factory_manager.cc
@@ -0,0 +1,312 @@ +// 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 "extensions/browser/url_loader_factory_manager.h" + +#include <algorithm> +#include <utility> +#include <vector> + +#include "base/containers/flat_set.h" +#include "base/stl_util.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/process_map.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_set.h" +#include "extensions/common/manifest_handlers/content_scripts_handler.h" +#include "extensions/common/user_script.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "url/gurl.h" +#include "url/origin.h" +#include "url/scheme_host_port.h" +#include "url/url_constants.h" + +namespace extensions { + +namespace { + +enum class FactoryUser { + kContentScript, + kExtensionProcess, +}; + +bool DoContentScriptsDependOnRelaxedCorb(const Extension& extension) { + // TODO(lukasza): https://crbug.com/846346: Return false if the + // |extension| doesn't need a special URLLoaderFactory based on + // Extensions.CrossOriginFetchFromContentScript2 Rappor data. + + // All modern extension manifests depend on relaxed CORB. + return extension.manifest_version() <= 2; +} + +bool DoExtensionPermissionsCoverCorsOrCorbRelatedOrigins( + const Extension& extension) { + // TODO(lukasza): https://crbug.com/846346: Return false if the |extension| + // doesn't need a special URLLoaderFactory based on |extension| permissions. + // For now we conservatively assume that all extensions need relaxed CORS/CORB + // treatment. + return true; +} + +bool IsSpecialURLLoaderFactoryRequired(const Extension& extension, + FactoryUser factory_user) { + switch (factory_user) { + case FactoryUser::kContentScript: + return DoContentScriptsDependOnRelaxedCorb(extension) && + DoExtensionPermissionsCoverCorsOrCorbRelatedOrigins(extension); + case FactoryUser::kExtensionProcess: + return DoExtensionPermissionsCoverCorsOrCorbRelatedOrigins(extension); + } +} + +network::mojom::URLLoaderFactoryPtrInfo CreateURLLoaderFactory( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const Extension& extension) { + // Compute relaxed CORB config to be used by |extension|. + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = process->GetID(); + // TODO(lukasza): https://crbug.com/846346: Use more granular CORB enforcement + // based on the specific |extension|'s permissions. + params->is_corb_enabled = false; + + // Create the URLLoaderFactory. + network::mojom::URLLoaderFactoryPtrInfo factory_info; + network_context->CreateURLLoaderFactory(mojo::MakeRequest(&factory_info), + std::move(params)); + return factory_info; +} + +// If |match_about_blank| is true, then traverses parent/opener chain until the +// first non-about-scheme document and returns its url. Otherwise, simply +// returns |document_url|. +// +// This function approximates ScriptContext::GetEffectiveDocumentURL from the +// renderer side. Unlike the renderer version of this code (in +// ScriptContext::GetEffectiveDocumentURL) the code below doesn't consider +// whether security origin of |frame| can access |next_candidate|. This is +// okay, because our only caller (DoesContentScriptMatchNavigation) expects +// false positives. +GURL GetEffectiveDocumentURL(content::RenderFrameHost* frame, + const GURL& document_url, + bool match_about_blank) { + base::flat_set<content::RenderFrameHost*> already_visited_frames; + + // Common scenario. If |match_about_blank| is false (as is the case in most + // extensions), or if the frame is not an about:-page, just return + // |document_url| (supposedly the URL of the frame). + if (!match_about_blank || !document_url.SchemeIs(url::kAboutScheme)) + return document_url; + + // Non-sandboxed about:blank and about:srcdoc pages inherit their security + // origin from their parent frame/window. So, traverse the frame/window + // hierarchy to find the closest non-about:-page and return its URL. + content::RenderFrameHost* found_frame = frame; + do { + DCHECK(found_frame); + already_visited_frames.insert(found_frame); + + // The loop should only execute (and consider the parent chain) if the + // currently considered frame has about: scheme. + DCHECK(match_about_blank); + DCHECK( + ((found_frame == frame) && document_url.SchemeIs(url::kAboutScheme)) || + (found_frame->GetLastCommittedURL().SchemeIs(url::kAboutScheme))); + + // Attempt to find |next_candidate| - either a parent of opener of + // |found_frame|. + content::RenderFrameHost* next_candidate = found_frame->GetParent(); + if (!next_candidate) { + next_candidate = + content::WebContents::FromRenderFrameHost(found_frame)->GetOpener(); + } + if (!next_candidate || + base::ContainsKey(already_visited_frames, next_candidate)) { + break; + } + + found_frame = next_candidate; + } while (found_frame->GetLastCommittedURL().SchemeIs(url::kAboutScheme)); + + if (found_frame == frame) + return document_url; // Not committed yet at ReadyToCommitNavigation time. + return found_frame->GetLastCommittedURL(); +} + +// If |user_script| will inject JavaScript content script into the target of +// |navigation|, then DoesContentScriptMatchNavigation returns true. Otherwise +// it may return either true or false. Note that this function ignores CSS +// content scripts. +// +// This function approximates a subset of checks from +// UserScriptSet::GetInjectionForScript (which runs in the renderer process). +// Unlike the renderer version, the code below doesn't consider ability to +// create an injection host or the results of ScriptInjector::CanExecuteOnFrame. +// Additionally the |effective_url| calculations are also only an approximation. +// This is okay, because we may return either true even if no content scripts +// would be injected (i.e. it is okay to create a special URLLoaderFactory when +// in reality the content script won't be injected and won't need the factory). +bool DoesContentScriptMatchNavigation(const UserScript& user_script, + content::NavigationHandle* navigation) { + // A special URLLoaderFactory is only needed for Javascript content scripts + // (and is never needed for CSS-only injections). + if (user_script.js_scripts().empty()) + return false; + + content::RenderFrameHost* frame = navigation->GetRenderFrameHost(); + GURL effective_url = GetEffectiveDocumentURL(frame, navigation->GetURL(), + user_script.match_about_blank()); + bool is_subframe = frame->GetParent(); + return user_script.MatchesDocument(effective_url, is_subframe); +} + +// If |extension|'s manifest declares that it may inject JavaScript content +// script into the target of |navigation|, then DoContentScriptsMatchNavigation +// returns true. Otherwise it may return either true or false. Note that this +// function ignores CSS content scripts. +bool DoContentScriptsMatchNavigation(const Extension& extension, + content::NavigationHandle* navigation) { + const UserScriptList& list = + ContentScriptsInfo::GetContentScripts(&extension); + return std::any_of(list.begin(), list.end(), + [navigation](const std::unique_ptr<UserScript>& script) { + return DoesContentScriptMatchNavigation(*script, + navigation); + }); +} + +void MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + content::RenderFrameHost* frame, + std::vector<url::Origin> request_initiators, + bool push_to_renderer_now) { + DCHECK(!request_initiators.empty()); + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + frame->MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + std::move(request_initiators), push_to_renderer_now); + } else { + // TODO(lukasza): In non-NetworkService implementation of CORB, make an + // exception only for specific extensions (e.g. based on process id, + // similarly to how r585124 does it for plugins). Doing so will likely + // interfere with Extensions.CrossOriginFetchFromContentScript2 Rappor + // metric, so this needs to wait until this metric is not needed anymore. + } +} + +} // namespace + +// static +void URLLoaderFactoryManager::ReadyToCommitNavigation( + content::NavigationHandle* navigation) { + content::RenderFrameHost* frame = navigation->GetRenderFrameHost(); + + std::vector<url::Origin> initiators_requiring_separate_factory; + const ExtensionRegistry* registry = + ExtensionRegistry::Get(frame->GetProcess()->GetBrowserContext()); + DCHECK(registry); // ReadyToCommitNavigation shouldn't run during shutdown. + for (const auto& it : registry->enabled_extensions()) { + const Extension& extension = *it; + if (!DoContentScriptsMatchNavigation(extension, navigation)) + continue; + + if (!IsSpecialURLLoaderFactoryRequired(extension, + FactoryUser::kContentScript)) + continue; + + initiators_requiring_separate_factory.push_back( + url::Origin::Create(extension.url())); + } + + if (!initiators_requiring_separate_factory.empty()) { + // At ReadyToCommitNavigation time there is no need to trigger an explicit + // push of URLLoaderFactoryBundle to the renderer - it is sufficient if the + // factories are pushed during the commit. + constexpr bool kPushToRendererNow = false; + + MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + frame, std::move(initiators_requiring_separate_factory), + kPushToRendererNow); + } +} + +// static +void URLLoaderFactoryManager::WillExecuteCode(content::RenderFrameHost* frame, + const HostID& host_id) { + if (host_id.type() != HostID::EXTENSIONS) + return; + + const ExtensionRegistry* registry = + ExtensionRegistry::Get(frame->GetProcess()->GetBrowserContext()); + DCHECK(registry); // WillExecuteCode shouldn't happen during shutdown. + const Extension* extension = + registry->enabled_extensions().GetByID(host_id.id()); + DCHECK(extension); // Guaranteed by the caller - see the doc comment. + + if (!IsSpecialURLLoaderFactoryRequired(*extension, + FactoryUser::kContentScript)) + return; + + // When WillExecuteCode runs, the frame already received the initial + // URLLoaderFactoryBundle - therefore we need to request a separate push + // below. This doesn't race with the ExtensionMsg_ExecuteCode message, + // because the URLLoaderFactoryBundle is sent to the renderer over + // content.mojom.FrameNavigationControl interface which is associated with the + // legacy IPC pipe (raciness will be introduced if that ever changes). + constexpr bool kPushToRendererNow = true; + + MarkInitiatorsAsRequiringSeparateURLLoaderFactory( + frame, {url::Origin::Create(extension->url())}, kPushToRendererNow); +} + +// static +network::mojom::URLLoaderFactoryPtrInfo URLLoaderFactoryManager::CreateFactory( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& initiator_origin) { + content::BrowserContext* browser_context = process->GetBrowserContext(); + const ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context); + DCHECK(registry); // CreateFactory shouldn't happen during shutdown. + + // Opaque origins normally don't inherit security properties of their + // precursor origins, but here opaque origins (e.g. think data: URIs) created + // by an extension should inherit CORS/CORB treatment of the extension. + url::SchemeHostPort precursor_origin = + initiator_origin.GetTupleOrPrecursorTupleIfOpaque(); + + // Don't create a factory for something that is not an extension. + if (precursor_origin.scheme() != kExtensionScheme) + return network::mojom::URLLoaderFactoryPtrInfo(); + + // Find the |extension| associated with |initiator_origin|. + const Extension* extension = + registry->enabled_extensions().GetByID(precursor_origin.host()); + if (!extension) { + // This may happen if an extension gets disabled between the time + // RenderFrameHost::MarkInitiatorAsRequiringSeparateURLLoaderFactory is + // called and the time + // ContentBrowserClient::CreateURLLoaderFactory is called. + return network::mojom::URLLoaderFactoryPtrInfo(); + } + + // Figure out if the factory is needed for content scripts VS extension + // renderer. + FactoryUser factory_user = FactoryUser::kContentScript; + ProcessMap* process_map = ProcessMap::Get(browser_context); + if (process_map->Contains(extension->id(), process->GetID())) + factory_user = FactoryUser::kExtensionProcess; + + // Create the factory (but only if really needed). + if (!IsSpecialURLLoaderFactoryRequired(*extension, factory_user)) + return network::mojom::URLLoaderFactoryPtrInfo(); + return CreateURLLoaderFactory(process, network_context, *extension); +} + +} // namespace extensions
diff --git a/extensions/browser/url_loader_factory_manager.h b/extensions/browser/url_loader_factory_manager.h new file mode 100644 index 0000000..8f33f07 --- /dev/null +++ b/extensions/browser/url_loader_factory_manager.h
@@ -0,0 +1,72 @@ +// 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 EXTENSIONS_BROWSER_URL_LOADER_FACTORY_MANAGER_H_ +#define EXTENSIONS_BROWSER_URL_LOADER_FACTORY_MANAGER_H_ + +#include "base/macros.h" +#include "content/public/browser/navigation_handle.h" +#include "extensions/common/host_id.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +namespace content { +class RenderFrameHost; +class RenderProcessHost; +} // namespace content + +namespace url { +class Origin; +} // namespace url + +namespace extensions { + +// This class manages URLLoaderFactory objects that handle network requests that +// require extension-specific permissions (related to relaxed CORB and CORS). +// +// See also https://crbug.com/846346 for motivation for having separate +// URLLoaderFactory objects for content scripts. +class URLLoaderFactoryManager { + public: + // Only static methods. + URLLoaderFactoryManager() = delete; + + // To be called before a navigation commits (to ensure that the renderer gets + // the special URLLoaderFactory before injecting content scripts declared in + // an extension manifest). + // + // This method will inspect all enabled extensions and ask RenderFrameHost to + // create separate URLLoaderFactory objects for the extensions that declare in + // their manifest desire to inject content scripts into the target of the + // |navigation|. + static void ReadyToCommitNavigation(content::NavigationHandle* navigation); + + // To be called before ExtensionMsg_ExecuteCode is sent to a renderer process + // (to ensure that the renderer gets the special URLLoaderFactory before + // injecting content script requested via chrome.tabs.executeScript). + // + // This method may ask RenderFrameHost to create a separate URLLoaderFactory + // object for extension identified by |host_id|. The caller needs to ensure + // that if |host_id.type() == HostID::EXTENSIONS|, then the extension with the + // given id exists and is enabled. + static void WillExecuteCode(content::RenderFrameHost* frame, + const HostID& host_id); + + // Creates a URLLoaderFactory that should be used for requests initiated from + // |process| by |initiator_origin|. Returns a "null" InterfacePtrInfo if the + // default, extensions-agnostic URLLoaderFactory should be used (if either + // |initiator_origin| is not associated with an extension, or the extension + // doesn't need a special URLLoaderFactory). + static network::mojom::URLLoaderFactoryPtrInfo CreateFactory( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& initiator_origin); + + private: + DISALLOW_COPY_AND_ASSIGN(URLLoaderFactoryManager); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_URL_LOADER_FACTORY_MANAGER_H_
diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc index f9276b90..14349e6 100644 --- a/extensions/common/user_script.cc +++ b/extensions/common/user_script.cc
@@ -7,6 +7,9 @@ #include <stddef.h> #include <stdint.h> +#include <memory> +#include <utility> + #include "base/atomic_sequence_num.h" #include "base/command_line.h" #include "base/pickle.h" @@ -47,10 +50,9 @@ // static const char UserScript::kFileExtension[] = ".user.js"; - // static int UserScript::GenerateUserScriptID() { - return g_user_script_id_generator.GetNext(); + return g_user_script_id_generator.GetNext(); } bool UserScript::IsURLUserScript(const GURL& url, @@ -168,6 +170,14 @@ return true; } +bool UserScript::MatchesDocument(const GURL& effective_document_url, + bool is_subframe) const { + if (is_subframe && !match_all_frames()) + return false; + + return MatchesURL(effective_document_url); +} + void UserScript::File::Pickle(base::Pickle* pickle) const { pickle->WriteString(url_.spec()); // Do not write path. It's not needed in the renderer.
diff --git a/extensions/common/user_script.h b/extensions/common/user_script.h index 0573eb662..6d4d25d 100644 --- a/extensions/common/user_script.h +++ b/extensions/common/user_script.h
@@ -5,6 +5,7 @@ #ifndef EXTENSIONS_COMMON_USER_SCRIPT_H_ #define EXTENSIONS_COMMON_USER_SCRIPT_H_ +#include <memory> #include <string> #include <vector> @@ -229,6 +230,13 @@ // otherwise. bool MatchesURL(const GURL& url) const; + // Returns true if the script should be applied to the given + // |effective_document_url| (calculated by the caller based on + // match_about_blank()| while also taking into account whether the document's + // frame |is_subframe| and what the |top_level_origin| is. + bool MatchesDocument(const GURL& effective_document_url, + bool is_subframe) const; + // Serializes the UserScript into a pickle. The content of the scripts and // paths to UserScript::Files will not be serialized! void Pickle(base::Pickle* pickle) const; @@ -327,7 +335,7 @@ // Information we need while removing scripts from a UserScriptLoader. struct UserScriptIDPair { UserScriptIDPair(int id, const HostID& host_id); - UserScriptIDPair(int id); + explicit UserScriptIDPair(int id); int id; HostID host_id;
diff --git a/extensions/renderer/module_system.cc b/extensions/renderer/module_system.cc index 0b0ed47e..73d390c5eb 100644 --- a/extensions/renderer/module_system.cc +++ b/extensions/renderer/module_system.cc
@@ -122,7 +122,7 @@ // // Prefer to use Mojo from C++ if possible rather than adding to this list. static const char* const kApisRequiringMojo[] = { - "mimeHandlerPrivate", "mojoPrivate", + "mediaPerceptionPrivate", "mimeHandlerPrivate", "mojoPrivate", }; for (const auto* api : kApisRequiringMojo) {
diff --git a/extensions/renderer/user_script_set.cc b/extensions/renderer/user_script_set.cc index 35657b8..40cf7f9 100644 --- a/extensions/renderer/user_script_set.cc +++ b/extensions/renderer/user_script_set.cc
@@ -212,13 +212,11 @@ injection_host.reset(new WebUIInjectionHost(host_id)); } - if (web_frame->Parent() && !script->match_all_frames()) - return injection; // Only match subframes if the script declared it. - GURL effective_document_url = ScriptContext::GetEffectiveDocumentURL( web_frame, document_url, script->match_about_blank()); - if (!script->MatchesURL(effective_document_url)) + bool is_subframe = web_frame->Parent(); + if (!script->MatchesDocument(effective_document_url, is_subframe)) return injection; std::unique_ptr<ScriptInjector> injector(
diff --git a/extensions/shell/browser/shell_content_browser_client.cc b/extensions/shell/browser/shell_content_browser_client.cc index 4bf301e..a864ad7 100644 --- a/extensions/shell/browser/shell_content_browser_client.cc +++ b/extensions/shell/browser/shell_content_browser_client.cc
@@ -38,6 +38,7 @@ #include "extensions/browser/info_map.h" #include "extensions/browser/io_thread_extension_message_filter.h" #include "extensions/browser/process_map.h" +#include "extensions/browser/url_loader_factory_manager.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/switches.h" @@ -312,6 +313,15 @@ return false; } +network::mojom::URLLoaderFactoryPtrInfo +ShellContentBrowserClient::CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) { + return URLLoaderFactoryManager::CreateFactory(process, network_context, + request_initiator); +} + ShellBrowserMainParts* ShellContentBrowserClient::CreateShellBrowserMainParts( const content::MainFunctionParams& parameters, ShellBrowserMainDelegate* browser_main_delegate) {
diff --git a/extensions/shell/browser/shell_content_browser_client.h b/extensions/shell/browser/shell_content_browser_client.h index 8a7b6205..03718221 100644 --- a/extensions/shell/browser/shell_content_browser_client.h +++ b/extensions/shell/browser/shell_content_browser_client.h
@@ -88,6 +88,11 @@ bool is_main_frame, ui::PageTransition page_transition, bool has_user_gesture) override; + network::mojom::URLLoaderFactoryPtrInfo + CreateURLLoaderFactoryForNetworkRequests( + content::RenderProcessHost* process, + network::mojom::NetworkContext* network_context, + const url::Origin& request_initiator) override; protected: // Subclasses may wish to provide their own ShellBrowserMainParts.
diff --git a/ios/chrome/browser/signin/ios_chrome_signin_client.mm b/ios/chrome/browser/signin/ios_chrome_signin_client.mm index a4bd985..8038830 100644 --- a/ios/chrome/browser/signin/ios_chrome_signin_client.mm +++ b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
@@ -28,8 +28,8 @@ SigninErrorController* signin_error_controller, scoped_refptr<content_settings::CookieSettings> cookie_settings, scoped_refptr<HostContentSettingsMap> host_content_settings_map) - : network_callback_helper_( - std::make_unique<WaitForNetworkCallbackHelper>()), + : network_callback_helper_(std::make_unique<WaitForNetworkCallbackHelper>( + GetApplicationContext()->GetNetworkConnectionTracker())), browser_state_(browser_state), signin_error_controller_(signin_error_controller), cookie_settings_(cookie_settings),
diff --git a/ios/web/public/web_task_traits.h b/ios/web/public/web_task_traits.h index 88dafe7..0529508 100644 --- a/ios/web/public/web_task_traits.h +++ b/ios/web/public/web_task_traits.h
@@ -40,13 +40,20 @@ // Posting to a WebThread must only be done after it was initialized (ref. // WebMainLoop::CreateThreads() phase). class WebTaskTraitsExtension { + using WebThreadIDFilter = + base::trait_helpers::RequiredEnumTraitFilter<WebThread::ID>; + using NonNestableFilter = + base::trait_helpers::BooleanTraitFilter<NonNestable>; + public: static constexpr uint8_t kExtensionId = base::TaskTraitsExtensionStorage::kFirstEmbedderExtensionId; - struct ValidTrait { - ValidTrait(WebThread::ID) {} - ValidTrait(NonNestable) {} + struct ValidTrait : public base::TaskTraits::ValidTrait { + using base::TaskTraits::ValidTrait::ValidTrait; + + ValidTrait(WebThread::ID); + ValidTrait(NonNestable); }; template < @@ -54,11 +61,9 @@ class CheckArgumentsAreValid = std::enable_if_t< base::trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value>> constexpr WebTaskTraitsExtension(ArgTypes... args) - : web_thread_(base::trait_helpers::GetValueFromArgList( - base::trait_helpers::RequiredEnumArgGetter<WebThread::ID>(), + : web_thread_(base::trait_helpers::GetTraitFromArgList<WebThreadIDFilter>( args...)), - nestable_(!base::trait_helpers::GetValueFromArgList( - base::trait_helpers::BooleanArgGetter<NonNestable>(), + nestable_(!base::trait_helpers::GetTraitFromArgList<NonNestableFilter>( args...)) {} constexpr base::TaskTraitsExtensionStorage Serialize() const {
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 8e0f17b..859ff648 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -1994,6 +1994,10 @@ } else if (!_currentURLLoadWasTrigerred) { [self ensureContainerViewCreated]; + // This method reloads last committed item, so make than item also pending. + self.sessionController.pendingItemIndex = + self.sessionController.lastCommittedItemIndex; + // TODO(crbug.com/796608): end the practice of calling |loadCurrentURL| // when it is possible there is no current URL. If the call performs // necessary initialization, break that out.
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm index 0f6aea5..31690cca 100644 --- a/ios/web/web_state/web_state_observer_inttest.mm +++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -499,14 +499,25 @@ EXPECT_FALSE((*context)->IsDownload()); EXPECT_FALSE((*context)->IsPost()); EXPECT_FALSE((*context)->GetError()); - // This should be false. Navigation is determined as renderer initiated - // because there is no pending item during the restoration (crbug.com/888021). - EXPECT_TRUE((*context)->IsRendererInitiated()); + if (GetWebClient()->IsSlimNavigationManagerEnabled()) { + // This should be false. Navigation is determined as renderer initiated + // because there is no pending item during the restoration + // (crbug.com/888021). + EXPECT_TRUE((*context)->IsRendererInitiated()); + } else { + EXPECT_FALSE((*context)->IsRendererInitiated()); + } ASSERT_FALSE((*context)->GetResponseHeaders()); ASSERT_TRUE(web_state->IsLoading()); - // Pending item is null during restoration (crbug.com/888021). + NavigationManager* navigation_manager = web_state->GetNavigationManager(); - ASSERT_FALSE(navigation_manager->GetPendingItem()); + if (GetWebClient()->IsSlimNavigationManagerEnabled()) { + // Pending item is null during restoration (crbug.com/888021). + ASSERT_FALSE(navigation_manager->GetPendingItem()); + } else { + ASSERT_TRUE(navigation_manager->GetPendingItem()); + EXPECT_EQ(url, navigation_manager->GetPendingItem()->GetURL()); + } } // Verifies correctness of |NavigationContext| (|arg1|) for restoration @@ -534,9 +545,14 @@ EXPECT_FALSE((*context)->IsDownload()); EXPECT_FALSE((*context)->IsPost()); EXPECT_FALSE((*context)->GetError()); - // This should be false. Navigation is determined as renderer initiated - // because there is no pending item during the restoration (crbug.com/888021). - EXPECT_TRUE((*context)->IsRendererInitiated()); + if (GetWebClient()->IsSlimNavigationManagerEnabled()) { + // This should be false. Navigation is determined as renderer initiated + // because there is no pending item during the restoration + // (crbug.com/888021). + EXPECT_TRUE((*context)->IsRendererInitiated()); + } else { + EXPECT_FALSE((*context)->IsRendererInitiated()); + } ASSERT_TRUE((*context)->GetResponseHeaders()); std::string actual_mime_type; (*context)->GetResponseHeaders()->GetMimeType(&actual_mime_type);
diff --git a/ios/web/web_state/web_state_unittest.mm b/ios/web/web_state/web_state_unittest.mm index b161ad81..9940592e 100644 --- a/ios/web/web_state/web_state_unittest.mm +++ b/ios/web/web_state/web_state_unittest.mm
@@ -60,6 +60,8 @@ } } // namespace +using wk_navigation_util::IsWKInternalUrl; + // WebStateTest is parameterized on this enum to test both the legacy // implementation of navigation manager and the experimental implementation. enum NavigationManagerChoice { @@ -367,16 +369,13 @@ EXPECT_TRUE(last_committed_item); EXPECT_TRUE(last_committed_item && last_committed_item->GetURL() == "http://www.0.com/"); - EXPECT_FALSE(navigation_manager->GetPendingItem()); EXPECT_EQ(0, navigation_manager->GetLastCommittedItemIndex()); - EXPECT_EQ(-1, navigation_manager->GetPendingItemIndex()); EXPECT_TRUE(navigation_manager->GetBackwardItems().empty()); EXPECT_EQ(std::max(navigation_manager->GetItemCount() - 1, 0), static_cast<int>(navigation_manager->GetForwardItems().size())); } // TODO(crbug.com/877671): Ensure that the following API work correctly: // - WebState::GetTitle - // - WebState::IsLoading // - WebState::GetLoadingProgress EXPECT_FALSE(web_state_ptr->IsCrashed()); EXPECT_FALSE(web_state_ptr->IsEvicted()); @@ -386,6 +385,7 @@ EXPECT_TRUE(visible_item && visible_item->GetURL() == "http://www.0.com/"); EXPECT_FALSE(navigation_manager->CanGoBack()); EXPECT_FALSE(navigation_manager->GetTransientItem()); + EXPECT_FALSE(IsWKInternalUrl(web_state_ptr->GetVisibleURL())); return restored; })); @@ -397,6 +397,13 @@ if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) { histogram_tester_.ExpectTotalCount(kRestoreNavigationTime, 1); } + + // Now wait until the last committed item is fully loaded. + EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{ + EXPECT_FALSE(IsWKInternalUrl(web_state_ptr->GetVisibleURL())); + + return !navigation_manager->GetPendingItem() && !web_state_ptr->IsLoading(); + })); } // Tests that if a saved session is provided when creating a new WebState, it is
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.mm b/ios/web_view/internal/signin/ios_web_view_signin_client.mm index 249d151..c878768f 100644 --- a/ios/web_view/internal/signin/ios_web_view_signin_client.mm +++ b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
@@ -7,6 +7,7 @@ #include "components/signin/core/browser/cookie_settings_util.h" #include "components/signin/core/browser/device_id_helper.h" #include "google_apis/gaia/gaia_auth_fetcher.h" +#include "ios/web_view/internal/app/application_context.h" #import "ios/web_view/internal/sync/cwv_sync_controller_internal.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -21,8 +22,9 @@ SigninErrorController* signin_error_controller, scoped_refptr<content_settings::CookieSettings> cookie_settings, scoped_refptr<HostContentSettingsMap> host_content_settings_map) - : network_callback_helper_( - std::make_unique<WaitForNetworkCallbackHelper>()), + : network_callback_helper_(std::make_unique<WaitForNetworkCallbackHelper>( + ios_web_view::ApplicationContext::GetInstance() + ->GetNetworkConnectionTracker())), pref_service_(pref_service), url_loader_factory_(url_loader_factory), cookie_manager_(cookie_manager),
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 2a9b06b..ef3d2e1 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -284,6 +284,14 @@ const base::Feature kD3D11VP9Decoder{"D3D11VP9Decoder", base::FEATURE_DISABLED_BY_DEFAULT}; +// Falls back to other decoders after audio/video decode error happens. The +// implementation may choose different strategies on when to fallback. See +// DecoderStream for details. When disabled, playback will fail immediately +// after a decode error happens. This can be useful in debugging and testing +// because the behavior is simpler and more predictable. +const base::Feature kFallbackAfterDecodeError{"FallbackAfterDecodeError", + base::FEATURE_ENABLED_BY_DEFAULT}; + // Manage and report MSE buffered ranges by PTS intervals, not DTS intervals. const base::Feature kMseBufferByPts{"MseBufferByPts", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index daadde72..d955557d 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -107,6 +107,7 @@ MEDIA_EXPORT extern const base::Feature kD3D11VP9Decoder; MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoder; MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting; +MEDIA_EXPORT extern const base::Feature kFallbackAfterDecodeError; MEDIA_EXPORT extern const base::Feature kHardwareSecureDecryption; MEDIA_EXPORT extern const base::Feature kLimitParallelMediaPreloading; MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream;
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc index 09790bb..062ad30 100644 --- a/media/filters/decoder_stream.cc +++ b/media/filters/decoder_stream.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" @@ -17,6 +18,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/limits.h" #include "media/base/media_log.h" +#include "media/base/media_switches.h" #include "media/base/timestamp_constants.h" #include "media/base/video_decoder.h" #include "media/base/video_frame.h" @@ -519,7 +521,8 @@ switch (status) { case DecodeStatus::DECODE_ERROR: - if (!decoder_produced_a_frame_) { + if (!decoder_produced_a_frame_ && + base::FeatureList::IsEnabled(kFallbackAfterDecodeError)) { pending_decode_requests_ = 0; // Prevent all pending decode requests and outputs from those requests
diff --git a/media/media_options.gni b/media/media_options.gni index 435a5c1..46eaa581 100644 --- a/media/media_options.gni +++ b/media/media_options.gni
@@ -186,12 +186,20 @@ _default_mojo_media_host = "browser" } else if (is_android) { # Both chrome for Android and cast for Android belongs to this case - _default_mojo_media_services = [ - "cdm", - "audio_decoder", - "video_decoder", - ] - _default_mojo_media_host = "gpu" + if (is_cast_audio_only) { + _default_mojo_media_services = [ + "cdm", + "audio_decoder", + ] + _default_mojo_media_host = "browser" + } else { + _default_mojo_media_services = [ + "cdm", + "audio_decoder", + "video_decoder", + ] + _default_mojo_media_host = "gpu" + } } else if (is_chromeos || is_mac || is_win) { _default_mojo_media_services = [ "video_decoder" ] _default_mojo_media_host = "gpu"
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni index f653481..6579bb0 100644 --- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -5,7 +5,6 @@ _typemap_imports = [ "//ash/public/interfaces/typemaps.gni", "//chrome/chrome_cleaner/interfaces/typemaps/typemaps.gni", - "//chrome/common/extensions/typemaps.gni", "//chrome/common/importer/typemaps.gni", "//chrome/common/media_router/mojo/typemaps.gni", "//chrome/typemaps.gni",
diff --git a/remoting/host/disconnect_window_win.cc b/remoting/host/disconnect_window_win.cc index 1cd366e..71e2b7b 100644 --- a/remoting/host/disconnect_window_win.cc +++ b/remoting/host/disconnect_window_win.cc
@@ -5,6 +5,7 @@ #include <stddef.h> #include <windows.h> +#include <cstdlib> #include <memory> #include "base/compiler_specific.h" @@ -122,6 +123,8 @@ bool has_hotkey_ = false; base::win::ScopedGDIObject<HPEN> border_pen_; + webrtc::DesktopVector mouse_position_; + base::WeakPtrFactory<DisconnectWindowWin> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DisconnectWindowWin); @@ -380,7 +383,16 @@ void DisconnectWindowWin::OnLocalMouseEvent( const webrtc::DesktopVector& position) { - ShowDialog(); + // Don't show the dialog if the position changes by ~1px in any direction. + // This will prevent the dialog from being reshown due to small movements + // caused by hardware/software issues which cause cursor drift or small + // vibrations in the environment around the remote host. + if (std::abs(position.x() - mouse_position_.x()) > 1 || + std::abs(position.y() - mouse_position_.y()) > 1) { + ShowDialog(); + } + + mouse_position_ = position; } void DisconnectWindowWin::OnLocalKeyboardEvent() {
diff --git a/remoting/protocol/webrtc_frame_scheduler_simple.cc b/remoting/protocol/webrtc_frame_scheduler_simple.cc index 068b50d..94ab605 100644 --- a/remoting/protocol/webrtc_frame_scheduler_simple.cc +++ b/remoting/protocol/webrtc_frame_scheduler_simple.cc
@@ -191,9 +191,11 @@ if (updated_area - updated_region_area_.Max() > kBigFrameThresholdPixels) { int expected_frame_size = updated_area * kEstimatedBytesPerMegapixel / kPixelsPerMegapixel; - base::TimeDelta expected_send_delay = base::TimeDelta::FromMicroseconds( - base::Time::kMicrosecondsPerSecond * expected_frame_size / - pacing_bucket_.rate()); + base::TimeDelta expected_send_delay = + pacing_bucket_.rate() ? base::TimeDelta::FromMicroseconds( + base::Time::kMicrosecondsPerSecond * + expected_frame_size / pacing_bucket_.rate()) + : base::TimeDelta::Max(); if (expected_send_delay > kTargetFrameInterval) { params_out->vpx_min_quantizer = kMaxQuantizer; }
diff --git a/services/media_session/BUILD.gn b/services/media_session/BUILD.gn index c2d97c5..6995402 100644 --- a/services/media_session/BUILD.gn +++ b/services/media_session/BUILD.gn
@@ -12,12 +12,15 @@ source_set("lib") { sources = [ + "audio_focus_manager.cc", + "audio_focus_manager.h", "media_session_service.cc", "media_session_service.h", ] deps = [ "//mojo/public/cpp/bindings", + "//services/media_session/public/mojom", ] public_deps = [ @@ -34,6 +37,7 @@ source_set("tests") { testonly = true sources = [ + "audio_focus_manager_unittest.cc", "media_session_service_unittest.cc", ] @@ -41,6 +45,8 @@ ":lib", "//base", "//base/test:test_support", + "//services/media_session/public/cpp/test:test_support", + "//services/media_session/public/mojom", "//services/service_manager/public/cpp/test:test_support", "//testing/gtest", ]
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc new file mode 100644 index 0000000..c9a8b63f --- /dev/null +++ b/services/media_session/audio_focus_manager.cc
@@ -0,0 +1,232 @@ +// 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 "services/media_session/audio_focus_manager.h" + +#include "base/atomic_sequence_num.h" +#include "base/memory/ptr_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "services/media_session/public/mojom/audio_focus.mojom.h" + +namespace media_session { + +namespace { + +// Generate a unique audio focus request ID for the audio focus request. The IDs +// are only handed out by the audio focus manager. +int GenerateAudioFocusRequestId() { + static base::AtomicSequenceNumber request_id; + return request_id.GetNext(); +} + +} // namespace + +// static +AudioFocusManager* AudioFocusManager::GetInstance() { + return base::Singleton<AudioFocusManager>::get(); +} + +AudioFocusManager::RequestResponse AudioFocusManager::RequestAudioFocus( + mojom::MediaSessionPtr media_session, + mojom::MediaSessionInfoPtr session_info, + mojom::AudioFocusType type, + base::Optional<RequestId> previous_id) { + DCHECK(!session_info.is_null()); + DCHECK(media_session.is_bound()); + + if (!audio_focus_stack_.empty() && + previous_id == audio_focus_stack_.back()->id() && + audio_focus_stack_.back()->audio_focus_type() == type && + audio_focus_stack_.back()->IsActive()) { + // Early returning if |media_session| is already on top (has focus) and is + // active. + return AudioFocusManager::RequestResponse(previous_id.value(), true); + } + + // If we have a previous ID then we should remove it from the stack. + if (previous_id.has_value()) + RemoveFocusEntryIfPresent(previous_id.value()); + + if (type == mojom::AudioFocusType::kGainTransientMayDuck) { + for (auto& old_session : audio_focus_stack_) + old_session->session()->StartDucking(); + } else { + for (auto& old_session : audio_focus_stack_) { + // If the session has the force duck bit set then we should duck the + // session instead of suspending it. + if (old_session->info()->force_duck) { + old_session->session()->StartDucking(); + } else { + old_session->session()->Suspend( + mojom::MediaSession::SuspendType::kSystem); + } + } + } + + audio_focus_stack_.push_back(std::make_unique<AudioFocusManager::StackRow>( + std::move(media_session), std::move(session_info), type, + previous_id ? *previous_id : GenerateAudioFocusRequestId())); + + AudioFocusManager::StackRow* row = audio_focus_stack_.back().get(); + row->session()->StopDucking(); + + // Notify observers that we were gained audio focus. + observers_.ForAllPtrs([&row, type](mojom::AudioFocusObserver* observer) { + observer->OnFocusGained(row->info().Clone(), type); + }); + + // We always grant the audio focus request but this may not always be the case + // in the future. + return AudioFocusManager::RequestResponse(row->id(), true); +} + +void AudioFocusManager::AbandonAudioFocus(RequestId id) { + if (audio_focus_stack_.empty()) + return; + + if (audio_focus_stack_.back()->id() != id) { + RemoveFocusEntryIfPresent(id); + return; + } + + auto row = std::move(audio_focus_stack_.back()); + audio_focus_stack_.pop_back(); + + if (audio_focus_stack_.empty()) { + // Notify observers that we lost audio focus. + observers_.ForAllPtrs([&row](mojom::AudioFocusObserver* observer) { + observer->OnFocusLost(row->info().Clone()); + }); + return; + } + + // Allow the top-most MediaSession having force duck to unduck even if + // it is not active. + for (auto iter = audio_focus_stack_.rbegin(); + iter != audio_focus_stack_.rend(); ++iter) { + if (!(*iter)->info()->force_duck) + continue; + + auto duck_row = std::move(*iter); + duck_row->session()->StopDucking(); + audio_focus_stack_.erase(std::next(iter).base()); + audio_focus_stack_.push_back(std::move(duck_row)); + return; + } + + // Only try to unduck the new MediaSession on top. The session might be + // still inactive but it will not be resumed (so it doesn't surprise the + // user). + audio_focus_stack_.back()->session()->StopDucking(); + + // Notify observers that we lost audio focus. + observers_.ForAllPtrs([&row](mojom::AudioFocusObserver* observer) { + observer->OnFocusLost(row->info().Clone()); + }); +} + +mojo::InterfacePtrSetElementId AudioFocusManager::AddObserver( + mojom::AudioFocusObserverPtr observer) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return observers_.AddPtr(std::move(observer)); +} + +mojom::AudioFocusType AudioFocusManager::GetFocusTypeForSession(RequestId id) { + for (auto& row : audio_focus_stack_) { + if (row->id() == id) + return row->audio_focus_type(); + } + + NOTREACHED(); + return mojom::AudioFocusType::kGain; +} + +void AudioFocusManager::RemoveObserver(mojo::InterfacePtrSetElementId id) { + observers_.RemovePtr(id); +} + +void AudioFocusManager::ResetForTesting() { + audio_focus_stack_.clear(); + observers_.CloseAll(); +} + +void AudioFocusManager::FlushForTesting() { + observers_.FlushForTesting(); + + for (auto& session : audio_focus_stack_) + session->FlushForTesting(); +} + +AudioFocusManager::AudioFocusManager() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +AudioFocusManager::~AudioFocusManager() = default; + +void AudioFocusManager::RemoveFocusEntryIfPresent( + AudioFocusManager::RequestId id) { + for (auto iter = audio_focus_stack_.begin(); iter != audio_focus_stack_.end(); + ++iter) { + if ((*iter)->id() == id) { + audio_focus_stack_.erase(iter); + break; + } + } +} + +AudioFocusManager::StackRow::StackRow(mojom::MediaSessionPtr session, + mojom::MediaSessionInfoPtr current_info, + mojom::AudioFocusType audio_focus_type, + AudioFocusManager::RequestId id) + : id_(id), + session_(std::move(session)), + current_info_(std::move(current_info)), + audio_focus_type_(audio_focus_type), + binding_(this) { + mojom::MediaSessionObserverPtr observer; + binding_.Bind(mojo::MakeRequest(&observer)); + + // Listen for mojo errors. + binding_.set_connection_error_handler(base::BindOnce( + &AudioFocusManager::StackRow::OnConnectionError, base::Unretained(this))); + session_.set_connection_error_handler(base::BindOnce( + &AudioFocusManager::StackRow::OnConnectionError, base::Unretained(this))); + + // Listen to info changes on the MediaSession. + session_->AddObserver(std::move(observer)); +}; + +AudioFocusManager::StackRow::~StackRow() = default; + +void AudioFocusManager::StackRow::MediaSessionInfoChanged( + mojom::MediaSessionInfoPtr info) { + current_info_ = std::move(info); +} + +mojom::MediaSession* AudioFocusManager::StackRow::session() { + return session_.get(); +} + +const mojom::MediaSessionInfoPtr& AudioFocusManager::StackRow::info() const { + return current_info_; +} + +bool AudioFocusManager::StackRow::IsActive() const { + return current_info_->state == mojom::MediaSessionInfo::SessionState::kActive; +} + +void AudioFocusManager::StackRow::FlushForTesting() { + session_.FlushForTesting(); + binding_.FlushForTesting(); +} + +void AudioFocusManager::StackRow::OnConnectionError() { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&AudioFocusManager::AbandonAudioFocus, + base::Unretained(AudioFocusManager::GetInstance()), id())); +} + +} // namespace media_session
diff --git a/services/media_session/audio_focus_manager.h b/services/media_session/audio_focus_manager.h new file mode 100644 index 0000000..048d0b64 --- /dev/null +++ b/services/media_session/audio_focus_manager.h
@@ -0,0 +1,122 @@ +// 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 SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_H_ +#define SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_H_ + +#include <list> +#include <unordered_map> + +#include "base/memory/singleton.h" +#include "base/threading/thread_checker.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" +#include "services/media_session/public/mojom/audio_focus.mojom.h" + +namespace media_session { + +class AudioFocusManager { + public: + using RequestId = int; + using RequestResponse = std::pair<RequestId, bool>; + + // Returns Chromium's internal AudioFocusManager. + static AudioFocusManager* GetInstance(); + + // Requests audio focus with |type| for the |media_session| with + // |session_info|. The function will return a |RequestResponse| which contains + // a |RequestId| and a boolean as to whether the request was successful. If a + // session wishes to request a different focus type it should provide its + // previous request id as |previous_id|. + RequestResponse RequestAudioFocus(mojom::MediaSessionPtr media_session, + mojom::MediaSessionInfoPtr session_info, + mojom::AudioFocusType type, + base::Optional<RequestId> previous_id); + + // Abandons audio focus for a request with |id|. The next request on top of + // the stack will be granted audio focus. + void AbandonAudioFocus(RequestId id); + + // Returns the current audio focus type for a request with |id|. + mojom::AudioFocusType GetFocusTypeForSession(RequestId id); + + // Adds/removes audio focus observers. + mojo::InterfacePtrSetElementId AddObserver(mojom::AudioFocusObserverPtr); + void RemoveObserver(mojo::InterfacePtrSetElementId); + + private: + friend struct base::DefaultSingletonTraits<AudioFocusManager>; + friend class AudioFocusManagerTest; + + // Media internals UI needs access to internal state. + friend class MediaInternalsAudioFocusTest; + friend class MediaInternals; + + // Flush for testing will flush any pending messages to the observers. + void FlushForTesting(); + + // Reset for testing will clear any built up internal state. + void ResetForTesting(); + + AudioFocusManager(); + ~AudioFocusManager(); + + void RemoveFocusEntryIfPresent(RequestId id); + + // Weak reference of managed observers. Observers are expected to remove + // themselves before being destroyed. + mojo::InterfacePtrSet<mojom::AudioFocusObserver> observers_; + + // StackRow is a MediaSessionObserver and holds a cached copy of the latest + // MediaSessionInfo associated with the MediaSession. By keeping the info + // cached and readily available we can make audio focus decisions quickly + // without waiting on MediaSessions. + class StackRow : public mojom::MediaSessionObserver { + public: + StackRow(mojom::MediaSessionPtr session, + mojom::MediaSessionInfoPtr current_info, + mojom::AudioFocusType audio_focus_type, + RequestId id); + ~StackRow() override; + + // mojom::MediaSessionObserver. + void MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) override; + + mojom::MediaSession* session(); + const mojom::MediaSessionInfoPtr& info() const; + mojom::AudioFocusType audio_focus_type() const { return audio_focus_type_; } + + bool IsActive() const; + int id() { return id_; } + + // Flush any pending mojo messages for testing. + void FlushForTesting(); + + private: + void OnConnectionError(); + + int id_; + mojom::MediaSessionPtr session_; + mojom::MediaSessionInfoPtr current_info_; + mojom::AudioFocusType audio_focus_type_; + mojo::Binding<mojom::MediaSessionObserver> binding_; + + DISALLOW_COPY_AND_ASSIGN(StackRow); + }; + + // A stack of Mojo interface pointers and their requested audio focus type. + // A MediaSession must abandon audio focus before its destruction. + std::list<std::unique_ptr<StackRow>> audio_focus_stack_; + + // Adding observers should happen on the same thread that the service is + // running on. + THREAD_CHECKER(thread_checker_); + + DISALLOW_COPY_AND_ASSIGN(AudioFocusManager); +}; + +} // namespace media_session + +#endif // SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_H_
diff --git a/services/media_session/audio_focus_manager_unittest.cc b/services/media_session/audio_focus_manager_unittest.cc new file mode 100644 index 0000000..98edc53 --- /dev/null +++ b/services/media_session/audio_focus_manager_unittest.cc
@@ -0,0 +1,573 @@ +// 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 "services/media_session/audio_focus_manager.h" + +#include <memory> + +#include "base/command_line.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "services/media_session/public/cpp/test/audio_focus_test_util.h" +#include "services/media_session/public/mojom/audio_focus.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media_session { + +namespace { + +static const base::Optional<AudioFocusManager::RequestId> kNoRequestId; + +static const AudioFocusManager::RequestId kNoFocusedSession = -1; + +class MockMediaSession : public mojom::MediaSession { + public: + MockMediaSession() = default; + explicit MockMediaSession(bool force_duck) : force_duck_(force_duck) {} + + ~MockMediaSession() override {} + + void Suspend(SuspendType suspend_type) override { + DCHECK_EQ(SuspendType::kSystem, suspend_type); + SetState(mojom::MediaSessionInfo::SessionState::kSuspended); + } + + void StartDucking() override { + is_ducking_ = true; + NotifyObservers(); + } + + void StopDucking() override { + is_ducking_ = false; + NotifyObservers(); + } + + void GetMediaSessionInfo(GetMediaSessionInfoCallback callback) override { + std::move(callback).Run(GetSessionInfoSync()); + } + + void AddObserver(mojom::MediaSessionObserverPtr observer) override { + observers_.AddPtr(std::move(observer)); + } + + void GetDebugInfo(GetDebugInfoCallback callback) override {} + + void BindToMojoRequest(mojo::InterfaceRequest<mojom::MediaSession> request) { + bindings_.AddBinding(this, std::move(request)); + } + + void SetState(mojom::MediaSessionInfo::SessionState state) { + state_ = state; + NotifyObservers(); + } + + bool has_observers() const { return !observers_.empty(); } + + void CloseAllObservers() { observers_.CloseAll(); } + + private: + mojom::MediaSessionInfoPtr GetSessionInfoSync() { + mojom::MediaSessionInfoPtr info(mojom::MediaSessionInfo::New()); + info->force_duck = force_duck_; + info->state = state_; + if (is_ducking_) + info->state = mojom::MediaSessionInfo::SessionState::kDucking; + return info; + } + + void NotifyObservers() { + observers_.ForAllPtrs([this](mojom::MediaSessionObserver* observer) { + observer->MediaSessionInfoChanged(GetSessionInfoSync()); + }); + + // This will flush all pending async messages on the observers. + observers_.FlushForTesting(); + } + + const bool force_duck_ = false; + bool is_ducking_ = false; + mojom::MediaSessionInfo::SessionState state_ = + mojom::MediaSessionInfo::SessionState::kInactive; + + mojo::InterfacePtrSet<mojom::MediaSessionObserver> observers_; + mojo::BindingSet<mojom::MediaSession> bindings_; +}; + +} // anonymous namespace + +class AudioFocusManagerTest : public testing::Test { + public: + AudioFocusManagerTest() = default; + + void SetUp() override { + // AudioFocusManager is a singleton so we should make sure we reset any + // state in between tests. + AudioFocusManager::GetInstance()->ResetForTesting(); + } + + void TearDown() override { + // Run pending tasks. + base::RunLoop().RunUntilIdle(); + } + + AudioFocusManager::RequestId GetAudioFocusedSession() const { + const AudioFocusManager* manager = AudioFocusManager::GetInstance(); + const auto& audio_focus_stack = manager->audio_focus_stack_; + + for (auto iter = audio_focus_stack.rbegin(); + iter != audio_focus_stack.rend(); ++iter) { + if ((*iter)->audio_focus_type() == mojom::AudioFocusType::kGain) + return (*iter)->id(); + } + return kNoFocusedSession; + } + + int GetTransientMaybeDuckCount() const { + const AudioFocusManager* manager = AudioFocusManager::GetInstance(); + const auto& audio_focus_stack = manager->audio_focus_stack_; + int count = 0; + + for (auto iter = audio_focus_stack.rbegin(); + iter != audio_focus_stack.rend(); ++iter) { + if ((*iter)->audio_focus_type() == + mojom::AudioFocusType::kGainTransientMayDuck) + ++count; + else + break; + } + + return count; + } + + void AbandonAudioFocus(AudioFocusManager::RequestId id) { + AudioFocusManager::GetInstance()->AbandonAudioFocus(id); + FlushForTesting(); + } + + AudioFocusManager::RequestId RequestAudioFocus( + MockMediaSession* session, + mojom::AudioFocusType audio_focus_type) { + return RequestAudioFocus(session, audio_focus_type, kNoRequestId); + } + + AudioFocusManager::RequestId RequestAudioFocus( + MockMediaSession* session, + mojom::AudioFocusType audio_focus_type, + base::Optional<AudioFocusManager::RequestId> previous_id) { + mojom::MediaSessionPtr media_session; + session->BindToMojoRequest(mojo::MakeRequest(&media_session)); + + AudioFocusManager::RequestResponse response = + AudioFocusManager::GetInstance()->RequestAudioFocus( + std::move(media_session), test::GetMediaSessionInfoSync(session), + audio_focus_type, previous_id); + + // If the audio focus was granted then we should set the session state to + // active. + if (response.second) + session->SetState(mojom::MediaSessionInfo::SessionState::kActive); + + FlushForTesting(); + return response.first; + } + + mojom::MediaSessionInfo::SessionState GetState(MockMediaSession* session) { + return test::GetMediaSessionInfoSync(session)->state; + } + + std::unique_ptr<test::TestAudioFocusObserver> CreateObserver() { + std::unique_ptr<test::TestAudioFocusObserver> observer = + std::make_unique<test::TestAudioFocusObserver>(); + + mojom::AudioFocusObserverPtr observer_ptr; + observer->BindToMojoRequest(mojo::MakeRequest(&observer_ptr)); + AudioFocusManager::GetInstance()->AddObserver(std::move(observer_ptr)); + + return observer; + } + + private: + void FlushForTesting() { + AudioFocusManager::GetInstance()->FlushForTesting(); + } + + base::test::ScopedTaskEnvironment task_environment_; + + DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerTest); +}; + +TEST_F(AudioFocusManagerTest, InstanceAvailableAndSame) { + AudioFocusManager* audio_focus_manager = AudioFocusManager::GetInstance(); + EXPECT_TRUE(!!audio_focus_manager); + EXPECT_EQ(audio_focus_manager, AudioFocusManager::GetInstance()); +} + +TEST_F(AudioFocusManagerTest, AddObserverOnRequest) { + MockMediaSession media_session_1; + EXPECT_FALSE(media_session_1.has_observers()); + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain, + kNoRequestId); + EXPECT_TRUE(media_session_1.has_observers()); +} + +TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_ReplaceFocusedEntry) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + MockMediaSession media_session_3; + + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_1)); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_2)); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive, + GetState(&media_session_3)); + + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id_1, GetAudioFocusedSession()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_2 = + RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id_2, GetAudioFocusedSession()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_3 = + RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id_3, GetAudioFocusedSession()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); +} + +TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_Duplicate) { + MockMediaSession media_session; + + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain, request_id); + EXPECT_EQ(request_id, GetAudioFocusedSession()); +} + +TEST_F(AudioFocusManagerTest, RequestAudioFocusGain_FromTransient) { + MockMediaSession media_session; + + AudioFocusManager::RequestId request_id = RequestAudioFocus( + &media_session, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain, request_id); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); +} + +TEST_F(AudioFocusManagerTest, RequestAudioFocusTransient_FromGain) { + MockMediaSession media_session; + + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + + RequestAudioFocus(&media_session, + mojom::AudioFocusType::kGainTransientMayDuck, request_id); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session)); +} + +TEST_F(AudioFocusManagerTest, RequestAudioFocusTransient_FromGainWhileDucking) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); + + RequestAudioFocus(&media_session_2, + mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + RequestAudioFocus(&media_session_1, + mojom::AudioFocusType::kGainTransientMayDuck, request_id); + EXPECT_EQ(2, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesFocusedEntry) { + MockMediaSession media_session; + + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + + AbandonAudioFocus(request_id); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); +} + +TEST_F(AudioFocusManagerTest, AbandonAudioFocus_NoAssociatedEntry) { + AbandonAudioFocus(kNoFocusedSession); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); +} + +TEST_F(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientEntry) { + MockMediaSession media_session; + + AudioFocusManager::RequestId request_id = RequestAudioFocus( + &media_session, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + + { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + AbandonAudioFocus(request_id); + + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_TRUE(observer->focus_lost_session_.Equals( + test::GetMediaSessionInfoSync(&media_session))); + } +} + +TEST_F(AudioFocusManagerTest, AbandonAudioFocus_WhileDuckingThenResume) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AbandonAudioFocus(request_id_1); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + + AbandonAudioFocus(request_id_2); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, AbandonAudioFocus_StopsDucking) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AbandonAudioFocus(request_id_2); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, DuckWhilePlaying) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + RequestAudioFocus(&media_session_2, + mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, GainSuspendsTransient) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + + RequestAudioFocus(&media_session_2, + mojom::AudioFocusType::kGainTransientMayDuck); + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); +} + +TEST_F(AudioFocusManagerTest, DuckWithMultipleTransients) { + MockMediaSession media_session_1; + MockMediaSession media_session_2; + MockMediaSession media_session_3; + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_2 = RequestAudioFocus( + &media_session_2, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AudioFocusManager::RequestId request_id_3 = RequestAudioFocus( + &media_session_3, mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AbandonAudioFocus(request_id_2); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AbandonAudioFocus(request_id_3); + EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesFocus) { + { + MockMediaSession media_session; + + AudioFocusManager::RequestId request_id = + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id, GetAudioFocusedSession()); + } + + // If the media session is destroyed without abandoning audio focus we do not + // know until we next interact with the manager. + MockMediaSession media_session; + RequestAudioFocus(&media_session, + mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); +} + +TEST_F(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesTransients) { + { + MockMediaSession media_session; + RequestAudioFocus(&media_session, + mojom::AudioFocusType::kGainTransientMayDuck); + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + } + + // If the media session is destroyed without abandoning audio focus we do not + // know until we next interact with the manager. + MockMediaSession media_session; + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + EXPECT_EQ(0, GetTransientMaybeDuckCount()); +} + +TEST_F(AudioFocusManagerTest, GainDucksForceDuck) { + MockMediaSession media_session_1(true /* force_duck */); + MockMediaSession media_session_2; + + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + + AudioFocusManager::RequestId request_id_2 = + RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain); + + EXPECT_EQ(request_id_2, GetAudioFocusedSession()); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); +} + +TEST_F(AudioFocusManagerTest, + AbandoningGainFocusRevokesTopMostForceDuckSession) { + MockMediaSession media_session_1(true /* force_duck */); + MockMediaSession media_session_2; + MockMediaSession media_session_3; + + AudioFocusManager::RequestId request_id_1 = + RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain); + RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain); + + AudioFocusManager::RequestId request_id_3 = + RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGain); + EXPECT_EQ(request_id_3, GetAudioFocusedSession()); + + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended, + GetState(&media_session_2)); + EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kDucking, + GetState(&media_session_1)); + + AbandonAudioFocus(request_id_3); + EXPECT_EQ(request_id_1, GetAudioFocusedSession()); +} + +TEST_F(AudioFocusManagerTest, AudioFocusObserver_AbandonNoop) { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + AbandonAudioFocus(kNoFocusedSession); + + EXPECT_EQ(kNoFocusedSession, GetAudioFocusedSession()); + EXPECT_TRUE(observer->focus_lost_session_.is_null()); +} + +TEST_F(AudioFocusManagerTest, AudioFocusObserver_RequestNoop) { + MockMediaSession media_session; + AudioFocusManager::RequestId request_id; + + { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + request_id = + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain); + + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_EQ(mojom::AudioFocusType::kGain, observer->focus_gained_type()); + EXPECT_FALSE(observer->focus_gained_session_.is_null()); + } + + { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain, request_id); + + EXPECT_EQ(request_id, GetAudioFocusedSession()); + EXPECT_TRUE(observer->focus_gained_session_.is_null()); + } +} + +TEST_F(AudioFocusManagerTest, AudioFocusObserver_TransientMayDuck) { + MockMediaSession media_session; + AudioFocusManager::RequestId request_id; + + { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + request_id = RequestAudioFocus( + &media_session, mojom::AudioFocusType::kGainTransientMayDuck); + + EXPECT_EQ(1, GetTransientMaybeDuckCount()); + EXPECT_EQ(mojom::AudioFocusType::kGainTransientMayDuck, + observer->focus_gained_type()); + EXPECT_FALSE(observer->focus_gained_session_.is_null()); + } + + { + std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver(); + AbandonAudioFocus(request_id); + + EXPECT_EQ(0, GetTransientMaybeDuckCount()); + EXPECT_TRUE(observer->focus_lost_session_.Equals( + test::GetMediaSessionInfoSync(&media_session))); + } +} + +} // namespace media_session
diff --git a/services/media_session/public/cpp/test/BUILD.gn b/services/media_session/public/cpp/test/BUILD.gn new file mode 100644 index 0000000..2b515f0 --- /dev/null +++ b/services/media_session/public/cpp/test/BUILD.gn
@@ -0,0 +1,19 @@ +# 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. + +component("test_support") { + output_name = "media_session_test_support_cpp" + + sources = [ + "audio_focus_test_util.cc", + "audio_focus_test_util.h", + ] + + deps = [ + "//base", + "//services/media_session/public/mojom", + ] + + defines = [ "IS_MEDIA_SESSION_TEST_SUPPORT_CPP_IMPL" ] +}
diff --git a/services/media_session/public/cpp/test/audio_focus_test_util.cc b/services/media_session/public/cpp/test/audio_focus_test_util.cc new file mode 100644 index 0000000..2361d67 --- /dev/null +++ b/services/media_session/public/cpp/test/audio_focus_test_util.cc
@@ -0,0 +1,76 @@ +// 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 "services/media_session/public/cpp/test/audio_focus_test_util.h" + +namespace media_session { +namespace test { + +namespace { + +void ReceivedSessionInfo(media_session::mojom::MediaSessionInfoPtr* info_out, + base::RepeatingClosure callback, + media_session::mojom::MediaSessionInfoPtr result) { + *info_out = std::move(result); + std::move(callback).Run(); +} + +} // namespace + +TestAudioFocusObserver::TestAudioFocusObserver() : binding_(this) {} + +TestAudioFocusObserver::~TestAudioFocusObserver() = default; + +void TestAudioFocusObserver::OnFocusGained( + media_session::mojom::MediaSessionInfoPtr session, + media_session::mojom::AudioFocusType type) { + focus_gained_type_ = type; + focus_gained_session_ = std::move(session); + + if (wait_for_gained_) + run_loop_.Quit(); +} + +void TestAudioFocusObserver::OnFocusLost( + media_session::mojom::MediaSessionInfoPtr session) { + focus_lost_session_ = std::move(session); + + if (wait_for_lost_) + run_loop_.Quit(); +} + +void TestAudioFocusObserver::WaitForGainedEvent() { + if (!focus_gained_session_.is_null()) + return; + + wait_for_gained_ = true; + run_loop_.Run(); +} + +void TestAudioFocusObserver::WaitForLostEvent() { + if (!focus_lost_session_.is_null()) + return; + + wait_for_lost_ = true; + run_loop_.Run(); +} + +void TestAudioFocusObserver::BindToMojoRequest( + media_session::mojom::AudioFocusObserverRequest request) { + binding_.Bind(std::move(request)); +} + +media_session::mojom::MediaSessionInfoPtr GetMediaSessionInfoSync( + media_session::mojom::MediaSession* session) { + media_session::mojom::MediaSessionInfoPtr session_info; + base::RunLoop run_loop; + + session->GetMediaSessionInfo(base::BindOnce( + &ReceivedSessionInfo, &session_info, run_loop.QuitClosure())); + + return session_info; +} + +} // namespace test +} // namespace media_session
diff --git a/services/media_session/public/cpp/test/audio_focus_test_util.h b/services/media_session/public/cpp/test/audio_focus_test_util.h new file mode 100644 index 0000000..01d120f --- /dev/null +++ b/services/media_session/public/cpp/test/audio_focus_test_util.h
@@ -0,0 +1,61 @@ +// 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 SERVICES_MEDIA_SESSION_PUBLIC_CPP_TEST_AUDIO_FOCUS_TEST_UTIL_H_ +#define SERVICES_MEDIA_SESSION_PUBLIC_CPP_TEST_AUDIO_FOCUS_TEST_UTIL_H_ + +#include "base/component_export.h" +#include "base/optional.h" +#include "base/run_loop.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/media_session/public/mojom/audio_focus.mojom.h" + +namespace media_session { +namespace test { + +class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) TestAudioFocusObserver + : public mojom::AudioFocusObserver { + public: + TestAudioFocusObserver(); + ~TestAudioFocusObserver() override; + + void OnFocusGained(media_session::mojom::MediaSessionInfoPtr, + media_session::mojom::AudioFocusType) override; + + void OnFocusLost(media_session::mojom::MediaSessionInfoPtr) override; + + void WaitForGainedEvent(); + void WaitForLostEvent(); + + media_session::mojom::AudioFocusType focus_gained_type() const { + DCHECK(!focus_gained_session_.is_null()); + return focus_gained_type_; + } + + void BindToMojoRequest(media_session::mojom::AudioFocusObserverRequest); + + // These store the values we received. + media_session::mojom::MediaSessionInfoPtr focus_gained_session_; + media_session::mojom::MediaSessionInfoPtr focus_lost_session_; + + private: + mojo::Binding<mojom::AudioFocusObserver> binding_; + media_session::mojom::AudioFocusType focus_gained_type_; + + // If either of these are true we will quit the run loop if we observe a gain + // or lost event. + bool wait_for_gained_ = false; + bool wait_for_lost_ = false; + + base::RunLoop run_loop_; +}; + +COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) +media_session::mojom::MediaSessionInfoPtr GetMediaSessionInfoSync( + media_session::mojom::MediaSession*); + +} // namespace test +} // namespace media_session + +#endif // SERVICES_MEDIA_SESSION_PUBLIC_CPP_TEST_AUDIO_FOCUS_TEST_UTIL_H_
diff --git a/services/media_session/public/mojom/audio_focus.mojom b/services/media_session/public/mojom/audio_focus.mojom index d6d88c72..5bf2ce5 100644 --- a/services/media_session/public/mojom/audio_focus.mojom +++ b/services/media_session/public/mojom/audio_focus.mojom
@@ -21,8 +21,8 @@ // The observer for audio focus events. interface AudioFocusObserver { // The given |session| gained audio focus with the specified |type|. - OnFocusGained(MediaSession session, AudioFocusType type); + OnFocusGained(MediaSessionInfo session, AudioFocusType type); // The given |session| lost audio focus. - OnFocusLost(MediaSession session); + OnFocusLost(MediaSessionInfo session); };
diff --git a/services/media_session/public/mojom/media_session.mojom b/services/media_session/public/mojom/media_session.mojom index 5624599d..9a156c8 100644 --- a/services/media_session/public/mojom/media_session.mojom +++ b/services/media_session/public/mojom/media_session.mojom
@@ -4,8 +4,79 @@ module media_session.mojom; +// Contains state information about a MediaSession. +struct MediaSessionInfo { + enum SessionState { + // The MediaSession is currently playing media. + kActive, + + // The MediaSession is currently playing at a reduced volume (ducking). + kDucking, + + // The MediaSession is currently paused. + kSuspended, + + // The MediaSession is not currently playing media. + kInactive, + }; + + // The current state of the MediaSession. + SessionState state; + + // If true then we will always duck this MediaSession instead of suspending. + bool force_duck; +}; + +// Contains debugging information about a MediaSession. This will be displayed +// on the Media Internals WebUI. +struct MediaSessionDebugInfo { + // A unique name for the MediaSession. + string name; + + // The owner of the MediaSession. + string owner; + + // State information stored in a string e.g. Ducked. + string state; +}; + +// The observer for observing media session events. +interface MediaSessionObserver { + // The info associated with the session changed. + MediaSessionInfoChanged(MediaSessionInfo info); +}; + // A MediaSession manages the media session and audio focus for a given // WebContents or ARC app. // TODO(https://crbug.com/875004): migrate media session from content/public // to mojo. -interface MediaSession {}; +interface MediaSession { + enum SuspendType { + // Suspended by the system because a transient sound needs to be played. + kSystem, + // Suspended by the UI. + kUI, + // Suspended by the page via script or user interaction. + kContent, + }; + + // Returns information about the MediaSession. + GetMediaSessionInfo() => (MediaSessionInfo info); + + // Returns debug information about the MediaSession. + GetDebugInfo() => (MediaSessionDebugInfo info); + + // Let the media session start ducking such that the volume multiplier + // is reduced. + StartDucking(); + + // Let the media session stop ducking such that the volume multiplier is + // recovered. + StopDucking(); + + // Suspend the media session. + // |type| represents the origin of the request. + Suspend(SuspendType suspend_type); + + AddObserver(MediaSessionObserver observer); +};
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc index 49e56c59..818a624 100644 --- a/services/network/network_context_unittest.cc +++ b/services/network/network_context_unittest.cc
@@ -3760,6 +3760,72 @@ "Failed: FindProxyForURL(url=http://server.bad.dns/)"); } +// Test ensures that ProxyServer data is populated correctly across Mojo calls. +// Basically it performs a set of URLLoader network requests, whose requests +// configure proxies. Then it checks whether the expected proxy scheme is +// respected. +TEST_F(NetworkContextTest, EnsureProperProxyServerIsUsed) { + net::test_server::EmbeddedTestServer test_server; + test_server.AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("services/test/data"))); + ASSERT_TRUE(test_server.Start()); + + struct ProxyConfigSet { + net::ProxyConfig proxy_config; + GURL url; + net::ProxyServer::Scheme expected_proxy_config_scheme; + } proxy_config_set[2]; + + proxy_config_set[0].proxy_config.proxy_rules().ParseFromString( + base::StringPrintf("http=%s", + test_server.host_port_pair().ToString().c_str())); + // The domain here is irrelevant, and it is the path that matters. + proxy_config_set[0].url = GURL("http://does.not.matter/echo"); + proxy_config_set[0].expected_proxy_config_scheme = + net::ProxyServer::SCHEME_HTTP; + + proxy_config_set[1].proxy_config.proxy_rules().ParseFromString( + "http=direct://"); + proxy_config_set[1].url = test_server.GetURL("/echo"); + proxy_config_set[1].expected_proxy_config_scheme = + net::ProxyServer::SCHEME_DIRECT; + + for (const auto& proxy_data : proxy_config_set) { + mojom::NetworkContextParamsPtr context_params = CreateContextParams(); + context_params->initial_proxy_config = net::ProxyConfigWithAnnotation( + proxy_data.proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS); + mojom::ProxyConfigClientPtr config_client; + context_params->proxy_config_client_request = + mojo::MakeRequest(&config_client); + std::unique_ptr<NetworkContext> network_context = + CreateContextWithParams(std::move(context_params)); + + mojom::URLLoaderFactoryPtr loader_factory; + mojom::URLLoaderFactoryParamsPtr params = + mojom::URLLoaderFactoryParams::New(); + params->process_id = 0; + network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory), + std::move(params)); + + ResourceRequest request; + // The domain here is irrelevant, and it is the path that matters. + request.url = proxy_data.url; // test_server.GetURL("/echo"); + + mojom::URLLoaderPtr loader; + TestURLLoaderClient client; + loader_factory->CreateLoaderAndStart( + mojo::MakeRequest(&loader), 0 /* routing_id */, 0 /* request_id */, + 0 /* options */, request, client.CreateInterfacePtr(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + + client.RunUntilComplete(); + + EXPECT_TRUE(client.has_received_completion()); + EXPECT_EQ(client.response_head().proxy_server.scheme(), + proxy_data.expected_proxy_config_scheme); + } +} + } // namespace } // namespace network
diff --git a/services/network/public/cpp/net_ipc_param_traits.cc b/services/network/public/cpp/net_ipc_param_traits.cc index 79912a5..445809f0 100644 --- a/services/network/public/cpp/net_ipc_param_traits.cc +++ b/services/network/public/cpp/net_ipc_param_traits.cc
@@ -222,6 +222,47 @@ l->append("<HttpResponseHeaders>"); } +void ParamTraits<net::ProxyServer>::Write(base::Pickle* m, + const param_type& p) { + net::ProxyServer::Scheme scheme = p.scheme(); + WriteParam(m, scheme); + // When scheme is either 'direct' or 'invalid' |host_port_pair| + // should not be called, as per the method implementation body. + if (scheme != net::ProxyServer::SCHEME_DIRECT && + scheme != net::ProxyServer::SCHEME_INVALID) { + WriteParam(m, p.host_port_pair()); + } + WriteParam(m, p.is_trusted_proxy()); +} + +bool ParamTraits<net::ProxyServer>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + net::ProxyServer::Scheme scheme; + bool is_trusted_proxy = false; + if (!ReadParam(m, iter, &scheme)) + return false; + + // When scheme is either 'direct' or 'invalid' |host_port_pair| + // should not be called, as per the method implementation body. + net::HostPortPair host_port_pair; + if (scheme != net::ProxyServer::SCHEME_DIRECT && + scheme != net::ProxyServer::SCHEME_INVALID && + !ReadParam(m, iter, &host_port_pair)) { + return false; + } + + if (!ReadParam(m, iter, &is_trusted_proxy)) + return false; + + *r = net::ProxyServer(scheme, host_port_pair, is_trusted_proxy); + return true; +} + +void ParamTraits<net::ProxyServer>::Log(const param_type& p, std::string* l) { + l->append("<ProxyServer>"); +} + void ParamTraits<net::OCSPVerifyResult>::Write(base::Pickle* m, const param_type& p) { WriteParam(m, p.response_status);
diff --git a/services/network/public/cpp/net_ipc_param_traits.h b/services/network/public/cpp/net_ipc_param_traits.h index 26b492c..9ae1082f 100644 --- a/services/network/public/cpp/net_ipc_param_traits.h +++ b/services/network/public/cpp/net_ipc_param_traits.h
@@ -13,6 +13,7 @@ #include "ipc/param_traits_macros.h" #include "net/base/auth.h" #include "net/base/host_port_pair.h" +#include "net/base/proxy_server.h" #include "net/base/request_priority.h" #include "net/cert/cert_verify_result.h" #include "net/cert/ct_policy_status.h" @@ -113,6 +114,16 @@ }; template <> +struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::ProxyServer> { + typedef net::ProxyServer param_type; + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ParamTraits<net::OCSPVerifyResult> { typedef net::OCSPVerifyResult param_type; static void Write(base::Pickle* m, const param_type& p); @@ -203,6 +214,9 @@ IPC_ENUM_TRAITS_MAX_VALUE( net::ct::CTPolicyCompliance, net::ct::CTPolicyCompliance::CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE) + +IPC_ENUM_TRAITS(net::ProxyServer::Scheme); // BitMask. + IPC_ENUM_TRAITS_MAX_VALUE(net::OCSPVerifyResult::ResponseStatus, net::OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR) IPC_ENUM_TRAITS_MAX_VALUE(net::OCSPRevocationStatus,
diff --git a/services/network/public/cpp/network_ipc_param_traits.h b/services/network/public/cpp/network_ipc_param_traits.h index 08b17f2..dcf4964 100644 --- a/services/network/public/cpp/network_ipc_param_traits.h +++ b/services/network/public/cpp/network_ipc_param_traits.h
@@ -13,6 +13,7 @@ #include "ipc/param_traits_macros.h" #include "net/base/auth.h" #include "net/base/host_port_pair.h" +#include "net/base/proxy_server.h" #include "net/base/request_priority.h" #include "net/cert/cert_verify_result.h" #include "net/cert/ct_policy_status.h" @@ -193,6 +194,7 @@ IPC_STRUCT_TRAITS_MEMBER(alpn_negotiated_protocol) IPC_STRUCT_TRAITS_MEMBER(socket_address) IPC_STRUCT_TRAITS_MEMBER(was_fetched_via_cache) + IPC_STRUCT_TRAITS_MEMBER(proxy_server) IPC_STRUCT_TRAITS_MEMBER(was_fetched_via_service_worker) IPC_STRUCT_TRAITS_MEMBER(was_fallback_required_by_service_worker) IPC_STRUCT_TRAITS_MEMBER(url_list_via_service_worker)
diff --git a/services/network/public/cpp/resource_response_info.h b/services/network/public/cpp/resource_response_info.h index e6b3b30..266a7bca 100644 --- a/services/network/public/cpp/resource_response_info.h +++ b/services/network/public/cpp/resource_response_info.h
@@ -15,6 +15,7 @@ #include "base/time/time.h" #include "net/base/host_port_pair.h" #include "net/base/load_timing_info.h" +#include "net/base/proxy_server.h" #include "net/cert/ct_policy_status.h" #include "net/cert/signed_certificate_timestamp_and_status.h" #include "net/http/http_response_headers.h" @@ -114,6 +115,9 @@ // True if the response was delivered through a proxy. bool was_fetched_via_proxy; + // The proxy server used for this request, if any. + net::ProxyServer proxy_server; + // True if the response was fetched by a ServiceWorker. bool was_fetched_via_service_worker;
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom index 41bf21ac..765ac8f 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom
@@ -322,11 +322,6 @@ // (even as an opaque int) in //services/network. See also the TODO comment // for network::ResourceRequest::resource_type. int32 corb_excluded_resource_type = -1; - // TODO(lukasza): https://crbug.com/846346: Replace the field below with a - // granular list of origins that content scripts can XHR into (based on - // extension manifest V3 / assumming that content scripts have a - // URLLoaderFactory separate from the rest of the renderer). - string corb_excluded_initiator_scheme; // True if web related security (e.g., CORS) should be disabled. This is // mainly used by people testing their sites, via a command line switch.
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 1594e7e..1f85c070 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -68,6 +68,7 @@ response->head.socket_address = response_info.socket_address; response->head.was_fetched_via_cache = request->was_cached(); response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); + response->head.proxy_server = request->proxy_server(); response->head.network_accessed = response_info.network_accessed; response->head.async_revalidation_requested = response_info.async_revalidation_requested; @@ -699,10 +700,7 @@ base::Unretained(this))); // Figure out if we need to sniff (for MIME type detection or for CORB). - if (factory_params_->is_corb_enabled && !is_nocors_corb_excluded_request_ && - (factory_params_->corb_excluded_initiator_scheme.empty() || - factory_params_->corb_excluded_initiator_scheme != - url_request->initiator().value_or(url::Origin()).scheme())) { + if (factory_params_->is_corb_enabled && !is_nocors_corb_excluded_request_) { CrossOriginReadBlocking::LogAction( CrossOriginReadBlocking::Action::kResponseStarted);
diff --git a/services/ws/public/mojom/window_manager.mojom b/services/ws/public/mojom/window_manager.mojom index 6a30b3b3..e0b2c8d 100644 --- a/services/ws/public/mojom/window_manager.mojom +++ b/services/ws/public/mojom/window_manager.mojom
@@ -34,11 +34,6 @@ // Type: int32_t. const string kContainerId_InitProperty = "init:container_id"; - // Disables the window manager from handling immersive fullscreen for the - // window. This is typically done if the client wants to handle immersive - // themselves. Type: bool. - const string kDisableImmersive_InitProperty = "init:disable_immersive"; - // The id of the display (display::Display::id()) to create the window on. // Type: int64. const string kDisplayId_InitProperty = "init:display_id";
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json index 84e472a0..f605621e 100644 --- a/testing/buildbot/chromium.perf.fyi.json +++ b/testing/buildbot/chromium.perf.fyi.json
@@ -294,7 +294,7 @@ "isolated_scripts": [ { "args": [ - "--benchmarks=blink_perf.layout_ng", + "--benchmarks=blink_perf.layout_ng,blink_perf.paint_layout_ng,loading.desktop_layout_ng", "-v", "--upload-results", "--output-format=histograms",
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter index e3c8d8f..e76edb77 100644 --- a/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter +++ b/testing/buildbot/filters/chromeos.single_process_mash.browser_tests.filter
@@ -232,10 +232,6 @@ # Flaky tests. crbug.com/880584 -UnifiedAutoplaySettingBrowserTest.* -# This seems to timeout only on the msan bot, but I suspect it could equally -# occur on the non-msan bots. https://crbug.com/891383 --UserAddingScreenTest.AddingSeveralUsers - # Window Open API tests. https://crbug.com/815379 -WindowOpenApiTest.OpenLockedFullscreenWindow -WindowOpenApiTest.RemoveLockedFullscreenFromWindow
diff --git a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter index dc367ea..3b4a718 100644 --- a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter +++ b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
@@ -231,6 +231,5 @@ -WebUIResourceBrowserTest.* -WebViewTest.* -WebrtcLoggingPrivateApiTest.* --WebstoreInlineInstallerTest.* -WindowAppleScriptTest.* -WindowOpenApiTest.*
diff --git a/testing/buildbot/filters/webui_polymer2_browser_tests.filter b/testing/buildbot/filters/webui_polymer2_browser_tests.filter index c959b96..58c4c28 100644 --- a/testing/buildbot/filters/webui_polymer2_browser_tests.filter +++ b/testing/buildbot/filters/webui_polymer2_browser_tests.filter
@@ -28,9 +28,6 @@ # Mac only failure. See crbug.com/876990 -CrSettingsPrivacyPageTest.All -# Tests that fail only on official builds. --CrSettingsIncompatibleApplicationsPageTest.All - # ChromeOS only test failures. -ActiveDirectoryJoinTest.TestActiveDirectoryEnrollment_DistinguishedName -ActiveDirectoryJoinTest.TestActiveDirectoryEnrollment_ErrorCard @@ -45,9 +42,6 @@ -CrSettingsFingerprintProgressArcTest.All -CrSettingsInternetDetailPageTest.InternetDetailPage -CrSettingsInternetPageTest.InternetPage --CrSettingsLanguagesPageTest.AddLanguagesDialog --CrSettingsLanguagesPageTest.LanguageMenu --CrSettingsLanguagesPageTest.Spellcheck -CrSettingsMultidevicePageTest.All -CrSettingsPeoplePageLockScreenTest.All -CrSettingsPeoplePageQuickUnlockAuthenticateTest.All @@ -150,7 +144,8 @@ CrSettingsFocusRowBehavior.* CrSettingsGoogleAssistantPageTest.* CrSettingsImportDataDialogTest.* -CrSettingsLanguagesPageTest.InputMethods +CrSettingsIncompatibleApplicationsPageTest.* +CrSettingsLanguagesPageTest.* CrSettingsLanguagesTest.* CrSettingsMainPageTest.* CrSettingsMenuTest.*
diff --git a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter index e8f5f670..e125069 100644 --- a/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter +++ b/testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter
@@ -13,7 +13,8 @@ CrSettingsSyncPageTest.All MaterialBookmarksFocusTest.All MaterialHistoryFocusTest.All -PrintPreviewDestinationDialogInteractiveTest.FocusSearchBox -PrintPreviewPrintHeaderInteractiveTest.FocusPrintOnReady +PrintPreviewDestinationDialogInteractiveTest.* +PrintPreviewNumberSettingsSectionInteractiveTest.BlurResetsEmptyInput PrintPreviewPagesSettingsTest.* +PrintPreviewPrintHeaderInteractiveTest.FocusPrintOnReady SettingsUIBrowserTest.All
diff --git a/testing/scripts/run_telemetry_benchmark_as_googletest.py b/testing/scripts/run_telemetry_benchmark_as_googletest.py index 1aa1c22..464ead6e 100755 --- a/testing/scripts/run_telemetry_benchmark_as_googletest.py +++ b/testing/scripts/run_telemetry_benchmark_as_googletest.py
@@ -126,6 +126,7 @@ cmd_args = cmd_args + [ '--story-filter=' + filter_regex ] + rc = 1 # Set default returncode in case there is an exception. try: cmd = [sys.executable] + cmd_args + [ '--output-dir', tempfile_dir,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 69b8363c6..6021bc27 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -5036,21 +5036,6 @@ ] } ], - "ViewsBrowserWindows": [ - { - "platforms": [ - "mac" - ], - "experiments": [ - { - "name": "Enabled_20180703", - "enable_features": [ - "ViewsBrowserWindows" - ] - } - ] - } - ], "VizDisplayCompositor": [ { "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees index 0d4ba7161..9b69c86 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -60,7 +60,6 @@ Bug(none) vibration/ [ Skip ] Bug(none) virtual/cors-rfc1918/ [ Skip ] Bug(none) virtual/custom-user-timing/ [ Skip ] -Bug(none) virtual/enable_wasm/ [ Skip ] Bug(none) virtual/exotic-color-space/ [ Skip ] Bug(none) virtual/feature-policy-permissions/ [ Skip ] Bug(none) virtual/feature-policy-vibrate/ [ Skip ] @@ -154,6 +153,7 @@ # Benign subpixel differences. Bug(none) transforms/3d/point-mapping/3d-point-mapping-deep.html [ Failure ] Bug(none) transforms/3d/point-mapping/3d-point-mapping-preserve-3d.html [ Failure ] +Bug(none) compositing/direct-image-compositing.html [ Failure ] Bug(none) compositing/masks/direct-image-mask.html [ Failure ] Bug(none) compositing/geometry/layer-due-to-layer-children.html [ Failure ] Bug(none) compositing/perpendicular-layer-sorting.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 index 6f821ef..05f7c4de8 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -68,7 +68,6 @@ Bug(none) virtual/android/ [ Skip ] Bug(none) virtual/blink-gen-property-trees/ [ Skip ] -Bug(none) virtual/enable_wasm/ [ Skip ] Bug(none) virtual/exotic-color-space/ [ Skip ] Bug(none) virtual/gpu/ [ Skip ] Bug(none) virtual/gpu-rasterization/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process index f1d40e3..753b6dd7 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process +++ b/third_party/WebKit/LayoutTests/FlagExpectations/site-per-process
@@ -105,8 +105,6 @@ Bug(none) virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/window-similar-but-cross-origin-success.sub.html [ Skip ] Bug(none) external/wpt/wasm/serialization/window-domain-success.sub.html [ Skip ] Bug(none) external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html [ Skip ] -Bug(none) virtual/enable_wasm/external/wpt/wasm/serialization/window-domain-success.sub.html [ Skip ] -Bug(none) virtual/enable_wasm/external/wpt/wasm/serialization/window-similar-but-cross-origin-success.sub.html [ Skip ] # Layout tests don't work for printing cross-site frames. crbug.com/822372 http/tests/printing/cross-site-frame.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests index 5d32c9dd..f3362ea 100644 --- a/third_party/WebKit/LayoutTests/NeverFixTests +++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -285,9 +285,6 @@ # Only Windows supports Symbol CMAP encoded fonts. crbug.com/627953 [ Android Linux Mac ] fast/text/symbol-cmap.html [ WontFix ] -# wasm tests. Currently under virtual/enable_wasm -crbug.com/642912 http/tests/wasm/ [ WontFix ] - # These tests require audio codecs which are generally not available; # these tests can still be run manually with --skipped=ignore. webaudio/codec-tests/aac [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index dcb69d3..7ba34e4 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -3917,7 +3917,6 @@ crbug.com/765738 [ Linux Win Mac ] http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Timeout ] -crbug.com/765738 [ Linux Win Mac ] virtual/enable_wasm/http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Timeout ] # ====== Random order flaky tests from here ====== # These tests are flaky when run in random order, which is the default on Linux & Mac since since 2016-12-16. @@ -4065,14 +4064,10 @@ crbug.com/874302 virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success-and-failure.html [ Timeout ] crbug.com/874302 virtual/sharedarraybuffer/external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/broadcastchannel-success.html [ Timeout ] crbug.com/874302 external/wpt/wasm/serialization/broadcastchannel-success-and-failure.html [ Timeout ] -crbug.com/874302 virtual/enable_wasm/external/wpt/wasm/serialization/broadcastchannel-success-and-failure.html [ Timeout ] crbug.com/874302 external/wpt/wasm/serialization/broadcastchannel-success.html [ Timeout ] -crbug.com/874302 virtual/enable_wasm/external/wpt/wasm/serialization/broadcastchannel-success.html [ Timeout ] crbug.com/877286 external/wpt/wasm/serialization/no-transferring.html [ Failure ] -crbug.com/877286 virtual/enable_wasm/external/wpt/wasm/serialization/no-transferring.html [ Failure ] crbug.com/877296 external/wpt/wasm/serialization/window-serviceworker-failure.https.html [ Failure ] -crbug.com/877296 virtual/enable_wasm/external/wpt/wasm/serialization/window-serviceworker-failure.https.html [ Failure ] crbug.com/831509 external/wpt/service-workers/service-worker/skip-waiting-installed.https.html [ Failure Pass ] crbug.com/831509 virtual/navigation-mojo-response/external/wpt/service-workers/service-worker/skip-waiting-installed.https.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites index bc5d0c1..7a71b181 100644 --- a/third_party/WebKit/LayoutTests/VirtualTestSuites +++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -327,16 +327,6 @@ "args": ["--force-device-scale-factor=1.5"] }, { - "prefix": "enable_wasm", - "base": "http/tests/wasm", - "args": ["--enable-features=WebAssembly"] - }, - { - "prefix": "enable_wasm", - "base": "external/wpt/wasm", - "args": ["--enable-features=WebAssembly"] - }, - { "prefix": "layout_ng", "base": "fast/block/basic", "args": ["--enable-blink-features=LayoutNG"]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.png new file mode 100644 index 0000000..9daec2d --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.txt new file mode 100644 index 0000000..9801d91 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip-expected.txt
@@ -0,0 +1,64 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "transform": 2 + }, + { + "name": "Child Containment Layer", + "position": [20, 20], + "bounds": [200, 200], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip.html b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip.html new file mode 100644 index 0000000..c7b8fe2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-clip.html
@@ -0,0 +1,9 @@ +<!DOCTYPE html> +<div style="margin: 100px; transform: rotate(45deg); overflow: hidden; + width: 200px; height: 200px; border: 20px solid green"> + <div style="will-change: transform; width: 400px; height: 400px; background: blue"></div> +</div> +<script> +if (window.testRunner) + testRunner.setCustomTextOutput(internals.layerTreeAsText(document)); +</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.png new file mode 100644 index 0000000..2aed34e1 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt new file mode 100644 index 0000000..a390599f --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
@@ -0,0 +1,94 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "opacity": 0.899999976158142, + "transform": 2 + }, + { + "name": "Ancestor Clipping Layer", + "position": [20, 20], + "bounds": [200, 200], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "position": [20, 0], + "bounds": [400, 100], + "contentsOpaque": true, + "backgroundColor": "#00FFFF", + "transform": 2 + }, + { + "name": "Ancestor Clipping Layer", + "position": [20, 20], + "bounds": [200, 200], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "position": [20, 20], + "bounds": [100, 400], + "contentsOpaque": true, + "backgroundColor": "#FF00FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave.html b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave.html new file mode 100644 index 0000000..0b6cd606 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-effect-interleave.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<div style="margin: 100px; width: 240px; height: 240px; transform: rotate(45deg)"> + <div style="opacity: 0.9"> + <div style="overflow: hidden; width: 200px; height: 200px; border: 20px solid green"> + <div style="will-change: transform; width: 400px; height: 400px; background: blue"></div> + <div style="position: fixed; top: 0; width: 400px; height: 100px; background: cyan"></div> + <div style="will-change: transform; z-index: 1; position: relative; top: -400px; width: 100px; height: 400px; background: magenta"></div> + </div> + </div> +</div> +<script> +if (window.testRunner) + testRunner.setCustomTextOutput(internals.layerTreeAsText(document)); +</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.png new file mode 100644 index 0000000..9daec2d --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.txt new file mode 100644 index 0000000..fbb4681 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-expected.txt
@@ -0,0 +1,64 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "transform": 2 + }, + { + "name": "Ancestor Clipping Layer", + "position": [20, 20], + "bounds": [200, 200], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png new file mode 100644 index 0000000..d4ef5fe --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt new file mode 100644 index 0000000..f97eb4f --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt
@@ -0,0 +1,82 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [300, 100], + "transform": 2 + }, + { + "name": "Ancestor Clipping Layer", + "bounds": [100, 100], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [200, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "bounds": [200, 22], + "contentsOpaque": true, + "backgroundColor": "#FFFF00", + "transform": 2 + }, + { + "name": "Ancestor Clipping Layer", + "bounds": [100, 100], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [50, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [150, 50] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave.html b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave.html new file mode 100644 index 0000000..f3af2dcd --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip-z-order-interleave.html
@@ -0,0 +1,12 @@ +<!DOCTYPE html> +<div style="margin: 100px; width: 300px; transform: rotate(45deg)"> + <div style="position: absolute; width: 200px; height: 22px; background: yellow; z-index: 1; will-change: transform"></div> + <div style="width: 100px; height: 100px; overflow: hidden"> + <div style="position: relative; width: 200px; height: 100px; background: green; will-change: transform"></div> + <div style="position: relative; top: -100px; z-index: 2; width: 50px; height: 200px; background: red; will-change: transform"></div> + </div> +</div> +<script> +if (window.testRunner) + testRunner.setCustomTextOutput(internals.layerTreeAsText(document)); +</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip.html b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip.html new file mode 100644 index 0000000..7172ca2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/rotate-then-clip.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<div style="margin: 100px; width: 240px; height: 240px; transform: rotate(45deg)"> + <div style="overflow: hidden; width: 200px; height: 200px; border: 20px solid green"> + <div style="will-change: transform; width: 400px; height: 400px; background: blue"></div> + </div> +</div> +<script> +if (window.testRunner) + testRunner.setCustomTextOutput(internals.layerTreeAsText(document)); +</script>
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-clip-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-clip-expected.txt new file mode 100644 index 0000000..101f13c --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-clip-expected.txt
@@ -0,0 +1,57 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt new file mode 100644 index 0000000..c087562 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
@@ -0,0 +1,80 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "opacity": 0.899999976158142, + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "position": [20, 0], + "bounds": [400, 100], + "contentsOpaque": true, + "backgroundColor": "#00FFFF", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "position": [20, 20], + "bounds": [100, 400], + "contentsOpaque": true, + "backgroundColor": "#FF00FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-expected.txt new file mode 100644 index 0000000..101f13c --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-expected.txt
@@ -0,0 +1,57 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png new file mode 100644 index 0000000..5b9c2a3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt new file mode 100644 index 0000000..c40716aa --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-gen-property-trees/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt
@@ -0,0 +1,70 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "drawsContent": false, + "backgroundColor": "#FFFFFF" + }, + { + "name": "Scrolling Layer", + "bounds": [800, 600], + "drawsContent": false + }, + { + "name": "Scrolling Contents Layer", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [300, 100], + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [200, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "bounds": [200, 22], + "contentsOpaque": true, + "backgroundColor": "#FFFF00", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [50, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [150, 50] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.png new file mode 100644 index 0000000..7bb3d1f43c --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.txt new file mode 100644 index 0000000..f437231 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-clip-expected.txt
@@ -0,0 +1,41 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt new file mode 100644 index 0000000..77428afc --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
@@ -0,0 +1,62 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "bounds": [240, 240], + "transform": 2 + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "position": [20, 0], + "bounds": [400, 100], + "contentsOpaque": true, + "backgroundColor": "#00FFFF", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "position": [20, 20], + "bounds": [100, 400], + "contentsOpaque": true, + "backgroundColor": "#FF00FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.png new file mode 100644 index 0000000..7bb3d1f43c --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.txt new file mode 100644 index 0000000..f437231 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-expected.txt
@@ -0,0 +1,41 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow DIV", + "position": [20, 20], + "bounds": [400, 400], + "contentsOpaque": true, + "backgroundColor": "#0000FF", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [120, 120] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png new file mode 100644 index 0000000..5b9c2a3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt new file mode 100644 index 0000000..53ca7f2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/flag-specific/enable-slimming-paint-v2/compositing/overflow/rotate-then-clip-z-order-interleave-expected.txt
@@ -0,0 +1,54 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "backgroundColor": "#FFFFFF" + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [200, 100], + "contentsOpaque": true, + "backgroundColor": "#008000", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "bounds": [200, 22], + "contentsOpaque": true, + "backgroundColor": "#FFFF00", + "transform": 2 + }, + { + "name": "LayoutBlockFlow (relative positioned) DIV", + "bounds": [50, 200], + "contentsOpaque": true, + "backgroundColor": "#FF0000", + "transform": 2 + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [108, 100, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [0.707106781186548, 0.707106781186548, 0, 0], + [-0.707106781186548, 0.707106781186548, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ], + "origin": [150, 50] + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/onunload.html b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/onunload.html new file mode 100644 index 0000000..46e36a64 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/onunload.html
@@ -0,0 +1,4 @@ +<html> +<body onunload="debugger"> +</body> +</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/page-with-unload.html b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/page-with-unload.html new file mode 100644 index 0000000..859daa6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/resources/page-with-unload.html
@@ -0,0 +1,2 @@ +<script>console.log('ready')</script> +<body onunload="debugger"></body> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation-expected.txt new file mode 100644 index 0000000..37c8fcb --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation-expected.txt
@@ -0,0 +1,7 @@ +Tests that we skip all pauses during navigation +Navigate page.. +Wait for ready message.. +done! +Script execution paused. +Script execution resumed. +
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation.js b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation.js new file mode 100644 index 0000000..7cd59c8 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/devtools/sources/debugger/skip-pause-during-navigation.js
@@ -0,0 +1,29 @@ +// 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. + +(async function() { + TestRunner.addResult( + `Tests that we skip all pauses during navigation`); + await TestRunner.loadModule('sources_test_runner'); + await TestRunner.loadModule('console_test_runner'); + await TestRunner.showPanel('sources'); + await SourcesTestRunner.startDebuggerTestPromise(); + await TestRunner.navigatePromise('resources/page-with-unload.html'); + TestRunner.addResult('Navigate page..'); + TestRunner.evaluateInPagePromise('window.location.href = window.location.href'); + TestRunner.addResult('Wait for ready message..'); + await ConsoleTestRunner.waitUntilMessageReceivedPromise(); + TestRunner.addResult('done!'); + + await TestRunner.addIframe('http://127.0.0.1:8000/devtools/sources/debugger/resources/onunload.html', { + name: 'myIFrame' + }); + + ConsoleTestRunner.changeExecutionContext('myIFrame'); + ConsoleTestRunner.evaluateInConsolePromise('window.location.href = window.location.href', true); + await SourcesTestRunner.waitUntilPausedPromise(); + SourcesTestRunner.resumeExecution(); + + SourcesTestRunner.completeDebuggerTest(); +})();
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_wasm/README.txt b/third_party/WebKit/LayoutTests/virtual/enable_wasm/README.txt deleted file mode 100644 index 5888cfc..0000000 --- a/third_party/WebKit/LayoutTests/virtual/enable_wasm/README.txt +++ /dev/null
@@ -1 +0,0 @@ -Tests that depend on --enable-features=WebAssembly
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt b/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt deleted file mode 100644 index 5888cfc..0000000 --- a/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt +++ /dev/null
@@ -1 +0,0 @@ -Tests that depend on --enable-features=WebAssembly
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_wasm/http/tests/wasm/README.txt b/third_party/WebKit/LayoutTests/virtual/enable_wasm/http/tests/wasm/README.txt deleted file mode 100644 index 5888cfc..0000000 --- a/third_party/WebKit/LayoutTests/virtual/enable_wasm/http/tests/wasm/README.txt +++ /dev/null
@@ -1 +0,0 @@ -Tests that depend on --enable-features=WebAssembly
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h index bafddd24..3d6721a 100644 --- a/third_party/blink/public/web/web_view.h +++ b/third_party/blink/public/web/web_view.h
@@ -272,9 +272,10 @@ // Returns the "preferred" contents size, defined as the preferred minimum // width of the main document's contents and the minimum height required to - // display the main document without scrollbars. The returned size has the - // page zoom factor applied. The lifecycle must be updated to at least layout - // before calling this (see: |UpdateLifecycle|). + // display the main document without scrollbars. If the document is in quirks + // mode (does not have <!doctype html>), the height will stretch to fill the + // viewport. The returned size has the page zoom factor applied. The lifecycle + // must be updated to at least layout before calling (see: |UpdateLifecycle|). virtual WebSize ContentsPreferredMinimumSize() = 0; // Sets the display mode of the web app.
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc index 3a57e97..2cd6c4c 100644 --- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc +++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -496,6 +496,8 @@ const ResourceError& error, WebHistoryCommitType commit_type) { web_frame_->DidFail(error, true, commit_type); + if (WebDevToolsAgentImpl* dev_tools = DevToolsAgent()) + dev_tools->DidFailProvisionalLoad(web_frame_->GetFrame()); virtual_time_pauser_.UnpauseVirtualTime(); }
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc index d1d9b39..04eb8da 100644 --- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc +++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc
@@ -412,12 +412,25 @@ resource_content_loader_->DidCommitLoadForLocalFrame(frame); for (auto& session : sessions_) session->DidCommitLoadForLocalFrame(frame); + if (inspected_frames_->Root() == frame) { + for (auto& session : sessions_) + session->V8Session()->setSkipAllPauses(false); + } +} + +void WebDevToolsAgentImpl::DidFailProvisionalLoad(LocalFrame* frame) { + if (inspected_frames_->Root() == frame) { + for (auto& session : sessions_) + session->V8Session()->setSkipAllPauses(false); + } } void WebDevToolsAgentImpl::DidStartProvisionalLoad(LocalFrame* frame) { if (inspected_frames_->Root() == frame) { - for (auto& session : sessions_) + for (auto& session : sessions_) { + session->V8Session()->setSkipAllPauses(true); session->V8Session()->resume(); + } } }
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h index acc14e3..e63f89aa 100644 --- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h +++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
@@ -83,6 +83,7 @@ // Instrumentation from web/ layer. void DidCommitLoadForLocalFrame(LocalFrame*); + void DidFailProvisionalLoad(LocalFrame*); void DidStartProvisionalLoad(LocalFrame*); bool ScreencastEnabled(); String NavigationInitiatorInfo(LocalFrame*);
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 c81ef4a..1ede952d 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -4075,6 +4075,24 @@ EXPECT_EQ(2, size.height); } +TEST_F(WebViewTest, PreferredMinimumSizeQuirksMode) { + WebViewImpl* web_view = web_view_helper_.Initialize(); + web_view->Resize(WebSize(800, 600)); + FrameTestHelpers::LoadHTMLString( + web_view->MainFrameImpl(), + R"HTML(<html> + <body style="margin: 0px;"> + <div style="width: 99px; height: 100px; display: inline-block;"></div> + </body> + </html>)HTML", + URLTestHelpers::ToKURL("http://example.com/")); + + WebSize size = web_view->ContentsPreferredMinimumSize(); + EXPECT_EQ(99, size.width); + // When in quirks mode the preferred height stretches to fill the viewport. + EXPECT_EQ(600, size.height); +} + TEST_F(WebViewTest, PreferredSizeWithGrid) { WebViewImpl* web_view = web_view_helper_.Initialize(); WebURL base_url = URLTestHelpers::ToKURL("http://example.com/");
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc index 83e35ee4..1c898a2 100644 --- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -453,7 +453,6 @@ : inspected_frames_(inspected_frames), v8_session_(v8_session), client_(client), - reloading_(false), inspector_resource_content_loader_(resource_content_loader), resource_content_loader_client_id_( resource_content_loader->CreateClientId()), @@ -546,7 +545,6 @@ stopScreencast(); - FinishReload(); return Response::OK(); } @@ -647,7 +645,6 @@ pending_script_to_evaluate_on_load_once_ = optional_script_to_evaluate_on_load.fromMaybe(""); v8_session_->setSkipAllPauses(true); - reloading_ = true; return Response::OK(); } @@ -717,13 +714,6 @@ return Response::OK(); } -void InspectorPageAgent::FinishReload() { - if (!reloading_) - return; - reloading_ = false; - v8_session_->setSkipAllPauses(false); -} - void InspectorPageAgent::GetResourceContentAfterResourcesContentLoaded( const String& frame_id, const String& url, @@ -910,7 +900,6 @@ void InspectorPageAgent::WillCommitLoad(LocalFrame*, DocumentLoader* loader) { if (loader->GetFrame() == inspected_frames_->Root()) { - FinishReload(); script_to_evaluate_on_load_once_ = pending_script_to_evaluate_on_load_once_; pending_script_to_evaluate_on_load_once_ = String(); }
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.h b/third_party/blink/renderer/core/inspector/inspector_page_agent.h index d241417c..65325e4 100644 --- a/third_party/blink/renderer/core/inspector/inspector_page_agent.h +++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
@@ -214,7 +214,6 @@ InspectorResourceContentLoader*, v8_inspector::V8InspectorSession*); - void FinishReload(); void GetResourceContentAfterResourcesContentLoaded( const String& frame_id, const String& url, @@ -242,7 +241,6 @@ Client* client_; String pending_script_to_evaluate_on_load_once_; String script_to_evaluate_on_load_once_; - bool reloading_; Member<InspectorResourceContentLoader> inspector_resource_content_loader_; int resource_content_loader_client_id_; InspectorAgentState::Boolean enabled_;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h index 2fc72fa5..3dc73c2 100644 --- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h +++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -35,47 +35,42 @@ explicit LayoutNGMixin(Element* element); ~LayoutNGMixin() override; - bool IsLayoutNGObject() const override { return true; } + bool IsLayoutNGObject() const final { return true; } - NGInlineNodeData* TakeNGInlineNodeData() override; - NGInlineNodeData* GetNGInlineNodeData() const override; - void ResetNGInlineNodeData() override; - bool HasNGInlineNodeData() const override { - return ng_inline_node_data_.get(); - } + NGInlineNodeData* TakeNGInlineNodeData() final; + NGInlineNodeData* GetNGInlineNodeData() const final; + void ResetNGInlineNodeData() final; + bool HasNGInlineNodeData() const final { return ng_inline_node_data_.get(); } - LayoutUnit FirstLineBoxBaseline() const override; - LayoutUnit InlineBlockBaseline(LineDirectionMode) const override; + LayoutUnit FirstLineBoxBaseline() const final; + LayoutUnit InlineBlockBaseline(LineDirectionMode) const final; - void InvalidateDisplayItemClients(PaintInvalidationReason) const override; + void InvalidateDisplayItemClients(PaintInvalidationReason) const final; - void Paint(const PaintInfo&) const override; + void Paint(const PaintInfo&) const final; bool NodeAtPoint(HitTestResult&, const HitTestLocation& location_in_container, const LayoutPoint& accumulated_offset, - HitTestAction) override; + HitTestAction) final; - PositionWithAffinity PositionForPoint(const LayoutPoint&) const override; + PositionWithAffinity PositionForPoint(const LayoutPoint&) const final; // Returns the last layout result for this block flow with the given // constraint space and break token, or null if it is not up-to-date or // otherwise unavailable. - scoped_refptr<NGLayoutResult> CachedLayoutResult( - const NGConstraintSpace&, - NGBreakToken*) const override; + scoped_refptr<NGLayoutResult> CachedLayoutResult(const NGConstraintSpace&, + NGBreakToken*) const final; void SetCachedLayoutResult(const NGConstraintSpace&, const NGBreakToken*, - const NGLayoutResult&) override; - void ClearCachedLayoutResult() override; + const NGLayoutResult&) final; + void ClearCachedLayoutResult() final; // For testing only. - scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting() override; + scoped_refptr<const NGLayoutResult> CachedLayoutResultForTesting() final; - NGPaintFragment* PaintFragment() const override { - return paint_fragment_.get(); - } + NGPaintFragment* PaintFragment() const final { return paint_fragment_.get(); } void SetPaintFragment(const NGBreakToken*, scoped_refptr<const NGPhysicalFragment>, NGPhysicalOffset) final; @@ -87,7 +82,7 @@ protected: bool IsOfType(LayoutObject::LayoutObjectType) const override; - void AddOverflowFromChildren() override; + void AddOverflowFromChildren() final; private: void AddScrollingOverflowFromChildren(); @@ -98,14 +93,14 @@ protected: void AddOutlineRects(Vector<LayoutRect>&, const LayoutPoint& additional_offset, - NGOutlineType) const override; + NGOutlineType) const final; - const NGPhysicalBoxFragment* CurrentFragment() const override; + const NGPhysicalBoxFragment* CurrentFragment() const final; const NGBaseline* FragmentBaseline(NGBaselineAlgorithmType) const; void DirtyLinesFromChangedChild(LayoutObject* child, - MarkingBehavior marking_behavior) override; + MarkingBehavior marking_behavior) final; std::unique_ptr<NGInlineNodeData> ng_inline_node_data_;
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h index cb5c14d..a060bb1 100644 --- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h +++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h
@@ -26,7 +26,6 @@ return StyleRef().ListStyleImage() && !StyleRef().ListStyleImage()->ErrorOccurred(); } - bool IsLayoutNGObject() const override { return true; } void UpdateMarkerTextIfNeeded() { if (marker_ && !is_marker_text_updated_ && !IsMarkerImage())
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h index c85d0a8..469de1b 100644 --- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h +++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_marker.h
@@ -33,8 +33,6 @@ bool IsContentImage() const; - bool IsLayoutNGObject() const override { return true; } - LayoutObject* SymbolMarkerLayoutText() const; // Marker text with suffix, e.g. "1. ", for use in accessibility.
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc index e0e178b..71000a9a 100644 --- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc +++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -266,6 +266,8 @@ paint_layer.SetPreviousPaintPhaseDescendantOutlinesEmpty(false); paint_layer.SetPreviousPaintPhaseFloatEmpty(false); paint_layer.SetPreviousPaintPhaseDescendantBlockBackgroundsEmpty(false); + context.paint_invalidator_context.subtree_flags |= + PaintInvalidatorContext::kSubtreeVisualRectUpdate; } bool PrePaintTreeWalk::NeedsTreeBuilderContextUpdate(
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc index ae162f6..c5758cd9 100644 --- a/third_party/blink/renderer/modules/cache_storage/cache.cc +++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -169,7 +169,7 @@ class Cache::BarrierCallbackForPut final : public GarbageCollectedFinalized<BarrierCallbackForPut> { public: - BarrierCallbackForPut(int number_of_operations, + BarrierCallbackForPut(wtf_size_t number_of_operations, Cache* cache, const String& method_name, ScriptPromiseResolver* resolver) @@ -181,7 +181,7 @@ batch_operations_.resize(number_of_operations); } - void OnSuccess(size_t index, + void OnSuccess(wtf_size_t index, mojom::blink::BatchOperationPtr batch_operation) { DCHECK_LT(index, batch_operations_.size()); if (!StillActive()) @@ -314,7 +314,7 @@ USING_GARBAGE_COLLECTED_MIXIN(BlobHandleCallbackForPut); public: - BlobHandleCallbackForPut(size_t index, + BlobHandleCallbackForPut(wtf_size_t index, BarrierCallbackForPut* barrier_callback, Request* request, Response* response) @@ -347,7 +347,7 @@ } private: - const size_t index_; + const wtf_size_t index_; Member<BarrierCallbackForPut> barrier_callback_; WebServiceWorkerRequest web_request_; @@ -361,7 +361,7 @@ public: CodeCacheHandleCallbackForPut(ScriptState* script_state, - size_t index, + wtf_size_t index, BarrierCallbackForPut* barrier_callback, Request* request, Response* response) @@ -432,7 +432,7 @@ private: const Member<ScriptState> script_state_; - const size_t index_; + const wtf_size_t index_; Member<BarrierCallbackForPut> barrier_callback_; const String mime_type_; @@ -708,7 +708,7 @@ request_infos.resize(requests.size()); Vector<ScriptPromise> promises; promises.resize(requests.size()); - for (size_t i = 0; i < requests.size(); ++i) { + for (wtf_size_t i = 0; i < requests.size(); ++i) { if (!requests[i]->url().ProtocolIsInHTTPFamily()) { return ScriptPromise::Reject(script_state, V8ThrowException::CreateTypeError( @@ -813,7 +813,7 @@ BarrierCallbackForPut* barrier_callback = new BarrierCallbackForPut(requests.size(), this, method_name, resolver); - for (size_t i = 0; i < requests.size(); ++i) { + for (wtf_size_t i = 0; i < requests.size(); ++i) { KURL url(NullURL(), requests[i]->url()); if (!url.ProtocolIsInHTTPFamily()) { barrier_callback->OnError("Request scheme '" + url.Protocol() +
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_test.cc b/third_party/blink/renderer/modules/cache_storage/cache_test.cc index 3209fd4da..e3fe6e8 100644 --- a/third_party/blink/renderer/modules/cache_storage/cache_test.cc +++ b/third_party/blink/renderer/modules/cache_storage/cache_test.cc
@@ -213,7 +213,7 @@ if (expected_batch_operations[i]->response) { ASSERT_EQ(expected_batch_operations[i]->response->url_list.size(), batch_operations[i]->response->url_list.size()); - for (size_t j = 0; + for (wtf_size_t j = 0; j < expected_batch_operations[i]->response->url_list.size(); ++j) { EXPECT_EQ(expected_batch_operations[i]->response->url_list[j], batch_operations[i]->response->url_list[j]);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc index 4ed61e6f..ba98460 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -360,11 +360,8 @@ } static bool LineDashSequenceIsValid(const Vector<double>& dash) { - for (size_t i = 0; i < dash.size(); i++) { - if (!std::isfinite(dash[i]) || dash[i] < 0) - return false; - } - return true; + return std::all_of(dash.begin(), dash.end(), + [](double d) { return std::isfinite(d) && d >= 0; }); } void BaseRenderingContext2D::setLineDash(const Vector<double>& dash) { @@ -1802,7 +1799,7 @@ CanvasColorParams(ColorParams().ColorSpace(), PixelFormat(), kNonOpaque); if (data_color_params.NeedsColorConversion(context_color_params) || PixelFormat() == kF16CanvasPixelFormat) { - unsigned data_length = + size_t data_length = data->Size().Area() * context_color_params.BytesPerPixel(); std::unique_ptr<uint8_t[]> converted_pixels(new uint8_t[data_length]); if (data->ImageDataInCanvasColorSettings(
diff --git a/third_party/blink/renderer/modules/crypto/crypto_key.cc b/third_party/blink/renderer/modules/crypto/crypto_key.cc index 6a37e56..8d2319a 100644 --- a/third_party/blink/renderer/modules/crypto/crypto_key.cc +++ b/third_party/blink/renderer/modules/crypto/crypto_key.cc
@@ -38,6 +38,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/to_v8.h" #include "third_party/blink/renderer/platform/crypto_result.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { @@ -122,7 +123,8 @@ void SetUint8Array(const char* property_name, const WebVector<unsigned char>& vector) override { builder_.Add(property_name, - DOMUint8Array::Create(vector.Data(), vector.size())); + DOMUint8Array::Create(vector.Data(), + SafeCast<wtf_size_t>(vector.size()))); } private: @@ -227,7 +229,7 @@ WebCryptoKeyUsageMask& mask, CryptoResult* result) { mask = 0; - for (size_t i = 0; i < usages.size(); ++i) { + for (wtf_size_t i = 0; i < usages.size(); ++i) { WebCryptoKeyUsageMask usage = KeyUsageStringToMask(usages[i]); if (!usage) { result->CompleteWithError(kWebCryptoErrorTypeType,
diff --git a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc index 013cfad..ab0355a 100644 --- a/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc +++ b/third_party/blink/renderer/modules/crypto/normalize_algorithm.cc
@@ -228,17 +228,18 @@ return String(); StringBuilder result; - const char* separator = ": "; + constexpr const char* separator = ": "; - size_t length = (messages_.size() - 1) * strlen(separator); - for (size_t i = 0; i < messages_.size(); ++i) + wtf_size_t length = (messages_.size() - 1) * strlen(separator); + for (wtf_size_t i = 0; i < messages_.size(); ++i) length += strlen(messages_[i]); result.ReserveCapacity(length); - for (size_t i = 0; i < messages_.size(); ++i) { + for (wtf_size_t i = 0; i < messages_.size(); ++i) { if (i) result.Append(separator, strlen(separator)); - result.Append(messages_[i], strlen(messages_[i])); + result.Append(messages_[i], + static_cast<wtf_size_t>(strlen(messages_[i]))); } return result.ToString();
diff --git a/third_party/blink/renderer/modules/eventsource/event_source_parser.cc b/third_party/blink/renderer/modules/eventsource/event_source_parser.cc index dfca4c7..39b80f1 100644 --- a/third_party/blink/renderer/modules/eventsource/event_source_parser.cc +++ b/third_party/blink/renderer/modules/eventsource/event_source_parser.cc
@@ -22,12 +22,12 @@ client_(client), codec_(NewTextCodec(UTF8Encoding())) {} -void EventSourceParser::AddBytes(const char* bytes, size_t size) { +void EventSourceParser::AddBytes(const char* bytes, uint32_t size) { // A line consists of |m_line| followed by // |bytes[start..(next line break)]|. - size_t start = 0; + uint32_t start = 0; const unsigned char kBOM[] = {0xef, 0xbb, 0xbf}; - for (size_t i = 0; i < size && !is_stopped_; ++i) { + for (uint32_t i = 0; i < size && !is_stopped_; ++i) { // As kBOM contains neither CR nor LF, we can think BOM and the line // break separately. if (is_recognizing_bom_ && line_.size() + (i - start) == arraysize(kBOM)) { @@ -77,8 +77,8 @@ event_type_ = g_null_atom; return; } - size_t field_name_end = line_.Find(':'); - size_t field_value_start; + wtf_size_t field_name_end = line_.Find(':'); + wtf_size_t field_value_start; if (field_name_end == WTF::kNotFound) { field_name_end = line_.size(); field_value_start = field_name_end; @@ -88,7 +88,7 @@ ++field_value_start; } } - size_t field_value_size = line_.size() - field_value_start; + wtf_size_t field_value_size = line_.size() - field_value_start; String field_name = FromUTF8(line_.data(), field_name_end); if (field_name == "event") { event_type_ = AtomicString( @@ -109,7 +109,8 @@ } if (field_name == "retry") { bool has_only_digits = true; - for (size_t i = field_value_start; i < line_.size() && has_only_digits; ++i) + for (wtf_size_t i = field_value_start; i < line_.size() && has_only_digits; + ++i) has_only_digits = IsASCIIDigit(line_[i]); if (field_value_start == line_.size()) { client_->OnReconnectionTimeSet(EventSource::kDefaultReconnectDelay); @@ -126,7 +127,7 @@ // Unrecognized field name. Ignore! } -String EventSourceParser::FromUTF8(const char* bytes, size_t size) { +String EventSourceParser::FromUTF8(const char* bytes, uint32_t size) { return codec_->Decode(bytes, size, WTF::FlushBehavior::kDataEOF); }
diff --git a/third_party/blink/renderer/modules/eventsource/event_source_parser.h b/third_party/blink/renderer/modules/eventsource/event_source_parser.h index 8c4ab1e..e3eb4e0 100644 --- a/third_party/blink/renderer/modules/eventsource/event_source_parser.h +++ b/third_party/blink/renderer/modules/eventsource/event_source_parser.h
@@ -31,7 +31,7 @@ EventSourceParser(const AtomicString& last_event_id, Client*); - void AddBytes(const char*, size_t); + void AddBytes(const char*, uint32_t); const AtomicString& LastEventId() const { return last_event_id_; } // Stop parsing. This can be called from Client::onMessageEvent. void Stop() { is_stopped_ = true; } @@ -39,7 +39,7 @@ private: void ParseLine(); - String FromUTF8(const char* bytes, size_t); + String FromUTF8(const char* bytes, uint32_t); Vector<char> line_;
diff --git a/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc b/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc index 0b10876..2370a04 100644 --- a/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc +++ b/third_party/blink/renderer/modules/eventsource/event_source_parser_test.cc
@@ -95,7 +95,9 @@ parser_(new EventSourceParser(AtomicString(), client_)) {} ~EventSourceParserTest() override = default; - void Enqueue(const char* data) { parser_->AddBytes(data, strlen(data)); } + void Enqueue(const char* data) { + parser_->AddBytes(data, static_cast<uint32_t>(strlen(data))); + } void EnqueueOneByOne(const char* data) { const char* p = data; while (*p != '\0')
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc index 771f1f8..4f763635 100644 --- a/third_party/blink/renderer/modules/exported/web_ax_object.cc +++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -90,8 +90,7 @@ void AddObjectVectorAttribute(AXObjectVectorAttribute attribute, HeapVector<Member<AXObject>>& value) override { WebVector<WebAXObject> result(value.size()); - for (size_t i = 0; i < value.size(); i++) - result[i] = WebAXObject(value[i]); + std::copy(value.begin(), value.end(), result.begin()); attribute_map_.AddObjectVectorAttribute( static_cast<WebAXObjectVectorAttribute>(attribute), result); } @@ -718,8 +717,8 @@ AXObject::AXObjectVector radio_buttons = private_->RadioButtonsInGroup(); WebVector<WebAXObject> web_radio_buttons(radio_buttons.size()); - for (size_t i = 0; i < radio_buttons.size(); ++i) - web_radio_buttons[i] = WebAXObject(radio_buttons[i]); + std::copy(radio_buttons.begin(), radio_buttons.end(), + web_radio_buttons.begin()); return web_radio_buttons; } @@ -915,10 +914,9 @@ WebString result = private_->GetName(name_from, &name_objects); out_name_from = name_from; - WebVector<WebAXObject> web_name_objects(name_objects.size()); - for (size_t i = 0; i < name_objects.size(); i++) - web_name_objects[i] = WebAXObject(name_objects[i]); - out_name_objects.Swap(web_name_objects); + out_name_objects.reserve(name_objects.size()); + out_name_objects.resize(name_objects.size()); + std::copy(name_objects.begin(), name_objects.end(), out_name_objects.begin()); return result; } @@ -946,10 +944,10 @@ private_->Description(name_from, description_from, &description_objects); out_description_from = description_from; - WebVector<WebAXObject> web_description_objects(description_objects.size()); - for (size_t i = 0; i < description_objects.size(); i++) - web_description_objects[i] = WebAXObject(description_objects[i]); - out_description_objects.Swap(web_description_objects); + out_description_objects.reserve(description_objects.size()); + out_description_objects.resize(description_objects.size()); + std::copy(description_objects.begin(), description_objects.end(), + out_description_objects.begin()); return result; } @@ -1076,13 +1074,7 @@ Vector<int> line_breaks_vector; private_->LineBreaks(line_breaks_vector); - - size_t vector_size = line_breaks_vector.size(); - WebVector<int> line_breaks_web_vector(vector_size); - for (size_t i = 0; i < vector_size; i++) - line_breaks_web_vector[i] = line_breaks_vector[i]; - result.Swap(line_breaks_web_vector); - + result = line_breaks_vector; return true; } @@ -1169,14 +1161,9 @@ AXObject::AXObjectVector headers; private_->RowHeaders(headers); - - size_t header_count = headers.size(); - WebVector<WebAXObject> result(header_count); - - for (size_t i = 0; i < header_count; i++) - result[i] = WebAXObject(headers[i]); - - row_header_elements.Swap(result); + row_header_elements.reserve(headers.size()); + row_header_elements.resize(headers.size()); + std::copy(headers.begin(), headers.end(), row_header_elements.begin()); } unsigned WebAXObject::ColumnIndex() const { @@ -1209,14 +1196,9 @@ AXObject::AXObjectVector headers; private_->ColumnHeaders(headers); - - size_t header_count = headers.size(); - WebVector<WebAXObject> result(header_count); - - for (size_t i = 0; i < header_count; i++) - result[i] = WebAXObject(headers[i]); - - column_header_elements.Swap(result); + column_header_elements.reserve(headers.size()); + column_header_elements.resize(headers.size()); + std::copy(headers.begin(), headers.end(), column_header_elements.begin()); } unsigned WebAXObject::CellColumnIndex() const { @@ -1307,7 +1289,7 @@ WebVector<ax::mojom::MarkerType> web_marker_types(marker_types.size()); WebVector<int> start_offsets(marker_ranges.size()); WebVector<int> end_offsets(marker_ranges.size()); - for (size_t i = 0; i < marker_types.size(); ++i) { + for (wtf_size_t i = 0; i < marker_types.size(); ++i) { web_marker_types[i] = ToAXMarkerType(marker_types[i]); DCHECK(marker_ranges[i].IsValid()); DCHECK_EQ(marker_ranges[i].Start().ContainerObject(), @@ -1327,12 +1309,7 @@ Vector<int> offsets_vector; private_->TextCharacterOffsets(offsets_vector); - - size_t vector_size = offsets_vector.size(); - WebVector<int> offsets_web_vector(vector_size); - for (size_t i = 0; i < vector_size; i++) - offsets_web_vector[i] = offsets_vector[i]; - offsets.Swap(offsets_web_vector); + offsets = offsets_vector; } void WebAXObject::GetWordBoundaries(WebVector<int>& starts, @@ -1345,7 +1322,7 @@ WebVector<int> word_start_offsets(word_boundaries.size()); WebVector<int> word_end_offsets(word_boundaries.size()); - for (size_t i = 0; i < word_boundaries.size(); ++i) { + for (wtf_size_t i = 0; i < word_boundaries.size(); ++i) { DCHECK(word_boundaries[i].IsValid()); DCHECK_EQ(word_boundaries[i].Start().ContainerObject(), word_boundaries[i].End().ContainerObject());
diff --git a/third_party/blink/renderer/modules/exported/web_idb_key.cc b/third_party/blink/renderer/modules/exported/web_idb_key.cc index 9b18332..600bfce 100644 --- a/third_party/blink/renderer/modules/exported/web_idb_key.cc +++ b/third_party/blink/renderer/modules/exported/web_idb_key.cc
@@ -28,6 +28,7 @@ #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h" #include "third_party/blink/renderer/modules/indexeddb/idb_key.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { @@ -36,7 +37,7 @@ } WebIDBKeyView WebIDBKeyArrayView::operator[](size_t index) const { - return WebIDBKeyView(private_->Array()[index].get()); + return WebIDBKeyView(private_->Array()[SafeCast<wtf_size_t>(index)].get()); } WebIDBKeyType WebIDBKeyView::KeyType() const { @@ -69,7 +70,7 @@ WebIDBKey WebIDBKey::CreateArray(WebVector<WebIDBKey> array) { IDBKey::KeyArray keys; - keys.ReserveCapacity(array.size()); + keys.ReserveCapacity(SafeCast<wtf_size_t>(array.size())); for (WebIDBKey& key : array) { DCHECK(key.View().KeyType() != kWebIDBKeyTypeNull); keys.emplace_back(key.ReleaseIdbKey());
diff --git a/third_party/blink/renderer/modules/filesystem/dom_file_path.cc b/third_party/blink/renderer/modules/filesystem/dom_file_path.cc index c50c536..d1ca351 100644 --- a/third_party/blink/renderer/modules/filesystem/dom_file_path.cc +++ b/third_party/blink/renderer/modules/filesystem/dom_file_path.cc
@@ -83,22 +83,22 @@ Vector<String> components; Vector<String> canonicalized; path.Split(DOMFilePath::kSeparator, components); - for (size_t i = 0; i < components.size(); ++i) { - if (components[i] == ".") + for (const auto& component : components) { + if (component == ".") continue; - if (components[i] == "..") { + if (component == "..") { if (canonicalized.size() > 0) canonicalized.pop_back(); continue; } - canonicalized.push_back(components[i]); + canonicalized.push_back(component); } if (canonicalized.IsEmpty()) return DOMFilePath::kRoot; StringBuilder result; - for (size_t i = 0; i < canonicalized.size(); ++i) { + for (const auto& component : canonicalized) { result.Append(DOMFilePath::kSeparator); - result.Append(canonicalized[i]); + result.Append(component); } return result.ToString(); } @@ -120,13 +120,10 @@ // ".." or "." is likely an attempt to break out of the sandbox. Vector<String> components; path.Split(DOMFilePath::kSeparator, components); - for (size_t i = 0; i < components.size(); ++i) { - if (components[i] == ".") - return false; - if (components[i] == "..") - return false; - } - return true; + return std::none_of(components.begin(), components.end(), + [](const String& component) { + return component == "." || component == ".."; + }); } bool DOMFilePath::IsValidName(const String& name) {
diff --git a/third_party/blink/renderer/modules/filesystem/file_writer.cc b/third_party/blink/renderer/modules/filesystem/file_writer.cc index 56e910f..6e0cc06 100644 --- a/third_party/blink/renderer/modules/filesystem/file_writer.cc +++ b/third_party/blink/renderer/modules/filesystem/file_writer.cc
@@ -179,7 +179,7 @@ operation_in_progress_ = kOperationNone; } - int num_aborts = num_aborts_; + long long num_aborts = num_aborts_; // We could get an abort in the handler for this event. If we do, it's // already handled the cleanup and signalCompletion call. double now = CurrentTimeMS();
diff --git a/third_party/blink/renderer/modules/filesystem/local_file_system.cc b/third_party/blink/renderer/modules/filesystem/local_file_system.cc index 0a7e003..f991a329 100644 --- a/third_party/blink/renderer/modules/filesystem/local_file_system.cc +++ b/third_party/blink/renderer/modules/filesystem/local_file_system.cc
@@ -51,6 +51,7 @@ #include "third_party/blink/renderer/platform/async_file_system_callbacks.h" #include "third_party/blink/renderer/platform/content_setting_callbacks.h" #include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { @@ -125,7 +126,7 @@ ScriptState::Scope scope(resolver_->GetScriptState()); if (return_multiple_) { Vector<ScriptPromise> result; - result.ReserveInitialCapacity(entries.size()); + result.ReserveInitialCapacity(SafeCast<wtf_size_t>(entries.size())); for (const auto& entry : entries) result.emplace_back(CreateFileHandle(entry)); resolver_->Resolve(ScriptPromise::All(resolver_->GetScriptState(), result)
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc index 51ace37..66209e1 100644 --- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc +++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -71,12 +71,12 @@ // A button press counts as a user activation if the button's value is greater // than the activation threshold. A threshold is used so that analog buttons // or triggers do not generate an activation from a light touch. - for (size_t pad_index = 0; pad_index < gamepads->length(); ++pad_index) { + for (wtf_size_t pad_index = 0; pad_index < gamepads->length(); ++pad_index) { Gamepad* pad = gamepads->item(pad_index); if (pad) { const GamepadButtonVector& buttons = pad->buttons(); - for (size_t i = 0; i < buttons.size(); ++i) { - double value = buttons.at(i)->value(); + for (auto button : buttons) { + double value = button->value(); if (value > kButtonActivationThreshold) return true; } @@ -88,7 +88,7 @@ } // namespace template <typename T> -static void SampleGamepad(size_t index, +static void SampleGamepad(unsigned index, T& gamepad, const device::Gamepad& device_gamepad, const TimeTicks& navigation_start) { @@ -144,7 +144,7 @@ GamepadDispatcher::Instance().SampleGamepads(gamepads); - for (size_t i = 0; i < device::Gamepads::kItemsLengthCap; ++i) { + for (unsigned i = 0; i < device::Gamepads::kItemsLengthCap; ++i) { device::Gamepad& web_gamepad = gamepads.items[i]; bool hide_xr_gamepad = false; @@ -390,7 +390,7 @@ bool NavigatorGamepad::CheckConnectedGamepads(GamepadList* old_gamepads, GamepadList* new_gamepads) { int disconnection_count = 0; - for (size_t i = 0; i < device::Gamepads::kItemsLengthCap; ++i) { + for (unsigned i = 0; i < device::Gamepads::kItemsLengthCap; ++i) { Gamepad* old_gamepad = old_gamepads ? old_gamepads->item(i) : nullptr; Gamepad* new_gamepad = new_gamepads->item(i); bool connected, disconnected;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc index 6a1da96d..db3a230 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -50,6 +50,7 @@ #include "third_party/blink/renderer/platform/histogram.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/atomics.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include <limits> #include <memory> @@ -192,7 +193,8 @@ WebVector<WebIDBObservation> web_observations, const WebIDBDatabaseCallbacks::TransactionMap& transactions) { HeapVector<Member<IDBObservation>> observations; - observations.ReserveInitialCapacity(web_observations.size()); + observations.ReserveInitialCapacity( + SafeCast<wtf_size_t>(web_observations.size())); for (WebIDBObservation& web_observation : web_observations) { observations.emplace_back( IDBObservation::Create(std::move(web_observation), isolate_));
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc b/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc index 67f5ea8..5504e12 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_event_dispatcher.cc
@@ -30,17 +30,18 @@ #include "third_party/blink/renderer/modules/event_modules.h" #include "third_party/blink/renderer/modules/event_target_modules.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { DispatchEventResult IDBEventDispatcher::Dispatch( Event& event, HeapVector<Member<EventTarget>>& event_targets) { - size_t size = event_targets.size(); + wtf_size_t size = event_targets.size(); DCHECK(size); event.SetEventPhase(Event::kCapturingPhase); - for (size_t i = size - 1; i; --i) { // Don't do the first element. + for (wtf_size_t i = size - 1; i; --i) { // Don't do the first element. event.SetCurrentTarget(event_targets[i].Get()); event_targets[i]->FireEventListeners(event); if (event.PropagationStopped()) @@ -54,7 +55,7 @@ goto doneDispatching; event.SetEventPhase(Event::kBubblingPhase); - for (size_t i = 1; i < size; ++i) { // Don't do the first element. + for (wtf_size_t i = 1; i < size; ++i) { // Don't do the first element. event.SetCurrentTarget(event_targets[i].Get()); event_targets[i]->FireEventListeners(event); if (event.PropagationStopped() || event.cancelBubble())
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key.cc b/third_party/blink/renderer/modules/indexeddb/idb_key.cc index 114db34..8ed421c 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_key.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_key.cc
@@ -41,8 +41,8 @@ return false; if (type_ == kArrayType) { - for (size_t i = 0; i < array_.size(); i++) { - if (!array_[i]->IsValid()) + for (const auto& element : array_) { + if (!element->IsValid()) return false; } } @@ -67,7 +67,8 @@ switch (type_) { case kArrayType: - for (size_t i = 0; i < array_.size() && i < other->array_.size(); ++i) { + for (wtf_size_t i = 0; i < array_.size() && i < other->array_.size(); + ++i) { if (int result = array_[i]->Compare(other->array_[i].get())) return result; } @@ -123,7 +124,7 @@ return static_cast<IDBKey*>(a)->IsLessThan(static_cast<IDBKey*>(b)); }); const auto end = std::unique(result.begin(), result.end()); - DCHECK_LE(static_cast<size_t>(end - result.begin()), result.size()); + DCHECK_LE(static_cast<wtf_size_t>(end - result.begin()), result.size()); result.resize(end - result.begin()); return result;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key_path.cc b/third_party/blink/renderer/modules/indexeddb/idb_key_path.cc index 3e1c12b..af493f27 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_key_path.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_key_path.cc
@@ -61,12 +61,12 @@ } bool IsIdentifier(const String& s) { - size_t length = s.length(); + wtf_size_t length = s.length(); if (!length) return false; if (!IsIdentifierStartCharacter(s[0])) return false; - for (size_t i = 1; i < length; ++i) { + for (wtf_size_t i = 1; i < length; ++i) { if (!IsIdentifierCharacter(s[i])) return false; } @@ -93,8 +93,8 @@ } key_path.Split('.', /*allow_empty_entries=*/true, elements); - for (size_t i = 0; i < elements.size(); ++i) { - if (!IsIdentifier(elements[i])) { + for (const auto& element : elements) { + if (!IsIdentifier(element)) { error = kIDBKeyPathParseErrorIdentifier; return; } @@ -110,8 +110,8 @@ IDBKeyPath::IDBKeyPath(const Vector<class String>& array) : type_(kArrayType), array_(array) { #if DCHECK_IS_ON() - for (size_t i = 0; i < array_.size(); ++i) - DCHECK(!array_[i].IsNull()); + for (const auto& element : array_) + DCHECK(!element.IsNull()); #endif } @@ -127,8 +127,8 @@ type_ = kArrayType; array_ = key_path.GetAsStringSequence(); #if DCHECK_IS_ON() - for (size_t i = 0; i < array_.size(); ++i) - DCHECK(!array_[i].IsNull()); + for (const auto& element : array_) + DCHECK(!element.IsNull()); #endif } } @@ -177,8 +177,8 @@ case kArrayType: if (array_.IsEmpty()) return false; - for (size_t i = 0; i < array_.size(); ++i) { - if (!IDBIsValidKeyPath(array_[i])) + for (const auto& element : array_) { + if (!IDBIsValidKeyPath(element)) return false; } return true;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_key_path_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_key_path_test.cc index 62dfa582..92e1f492 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_key_path_test.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_key_path_test.cc
@@ -49,7 +49,7 @@ if (error != kIDBKeyPathParseErrorNone) return; ASSERT_EQ(expected.size(), key_path_elements.size()); - for (size_t i = 0; i < expected.size(); ++i) + for (wtf_size_t i = 0; i < expected.size(); ++i) ASSERT_TRUE(expected[i] == key_path_elements[i]) << i; }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc index edacac1..3884f42 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
@@ -29,6 +29,7 @@ #include "base/feature_list.h" #include "base/memory/scoped_refptr.h" +#include "base/numerics/safe_conversions.h" #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h" #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h" #include "third_party/blink/public/platform/modules/indexeddb/web_idb_key_range.h" @@ -568,8 +569,10 @@ script_state->GetIsolate(), *it.value, clone)); } // Records 1KB to 1GB. - UMA_HISTOGRAM_COUNTS_1M("WebCore.IndexedDB.PutValueSize2", - value_wrapper.DataLengthBeforeWrapInBytes() / 1024); + UMA_HISTOGRAM_COUNTS_1M( + "WebCore.IndexedDB.PutValueSize2", + base::saturated_cast<base::HistogramBase::Sample>( + value_wrapper.DataLengthBeforeWrapInBytes() / 1024)); IDBRequest* request = IDBRequest::Create( script_state, source, transaction_.Get(), std::move(metrics));
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request.cc b/third_party/blink/renderer/modules/indexeddb/idb_request.cc index 9729f0d..765b626 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_request.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_request.cc
@@ -430,8 +430,8 @@ } DOMStringList* dom_string_list = DOMStringList::Create(); - for (size_t i = 0; i < string_list.size(); ++i) - dom_string_list->Append(string_list[i]); + for (const auto& item : string_list) + dom_string_list->Append(item); EnqueueResultInternal(IDBAny::Create(dom_string_list)); metrics_.RecordAndReset(); }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_test_helper.cc b/third_party/blink/renderer/modules/indexeddb/idb_test_helper.cc index 7e8b391..cf37a961 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_test_helper.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_test_helper.cc
@@ -36,9 +36,9 @@ std::unique_ptr<IDBValue> CreateIDBValueForTesting(v8::Isolate* isolate, bool create_wrapped_value) { - size_t element_count = create_wrapped_value ? 16 : 2; + uint32_t element_count = create_wrapped_value ? 16 : 2; v8::Local<v8::Array> v8_array = v8::Array::New(isolate, element_count); - for (size_t i = 0; i < element_count; ++i) + for (uint32_t i = 0; i < element_count; ++i) v8_array->Set(i, v8::True(isolate)); NonThrowableExceptionState non_throwable_exception_state;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value.cc b/third_party/blink/renderer/modules/indexeddb/idb_value.cc index cf13a66..21626735 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_value.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_value.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/public/platform/web_blob_info.h" #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "v8/include/v8.h" namespace blink { @@ -20,7 +21,7 @@ IDBValue::IDBValue(const WebData& data, const WebVector<WebBlobInfo>& web_blob_info) : data_(data) { - blob_info_.ReserveInitialCapacity(web_blob_info.size()); + blob_info_.ReserveInitialCapacity(SafeCast<wtf_size_t>(web_blob_info.size())); for (const WebBlobInfo& info : web_blob_info) { blob_info_.push_back(info);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.cc b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.cc index 7630571..5ab0aac 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/renderer/modules/indexeddb/idb_request.h" #include "third_party/blink/renderer/modules/indexeddb/idb_value.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { @@ -136,7 +137,7 @@ DCHECK(owns_wire_bytes_) << __func__ << " called after TakeWireBytes()"; #endif // DCHECK_IS_ON() - unsigned wire_data_size = wire_data_.size(); + size_t wire_data_size = wire_data_.size(); if (wire_data_size <= max_bytes) return false; @@ -154,7 +155,8 @@ wire_data_buffer_.push_back(kVersionTag); wire_data_buffer_.push_back(kRequiresProcessingSSVPseudoVersion); wire_data_buffer_.push_back(kReplaceWithBlob); - IDBValueWrapper::WriteVarInt(wire_data_size, wire_data_buffer_); + IDBValueWrapper::WriteVarInt(SafeCast<unsigned>(wire_data_size), + wire_data_buffer_); IDBValueWrapper::WriteVarInt(serialized_value_->BlobDataHandles().size(), wire_data_buffer_);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc index e52b0d13..f78b2600 100644 --- a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc +++ b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping_test.cc
@@ -184,7 +184,7 @@ // Friend class of IDBValueUnwrapper with access to its internals. class IDBValueUnwrapperReadTestHelper { public: - void ReadVarInt(const char* start, size_t buffer_size) { + void ReadVarInt(const char* start, uint32_t buffer_size) { IDBValueUnwrapper unwrapper; const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start); @@ -197,10 +197,10 @@ << "ReadVarInt should not change end_"; ASSERT_LE(unwrapper.current_, unwrapper.end_) << "ReadVarInt should not move current_ past end_"; - consumed_bytes_ = unwrapper.current_ - buffer_start; + consumed_bytes_ = static_cast<uint32_t>(unwrapper.current_ - buffer_start); } - void ReadBytes(const char* start, size_t buffer_size) { + void ReadBytes(const char* start, uint32_t buffer_size) { IDBValueUnwrapper unwrapper; const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start); @@ -212,7 +212,7 @@ ASSERT_EQ(unwrapper.end_, buffer_end) << "ReadBytes should not change end_"; ASSERT_LE(unwrapper.current_, unwrapper.end_) << "ReadBytes should not move current_ past end_"; - consumed_bytes_ = unwrapper.current_ - buffer_start; + consumed_bytes_ = static_cast<uint32_t>(unwrapper.current_ - buffer_start); } bool success() { return success_; } @@ -488,7 +488,8 @@ wrapped_value->SetIsolate(scope.GetIsolate()); EXPECT_TRUE(IDBValueUnwrapper::IsWrapped(wrapped_value.get())); - Vector<char> wrapped_marker_bytes(wrapped_marker_buffer->size()); + Vector<char> wrapped_marker_bytes( + static_cast<wtf_size_t>(wrapped_marker_buffer->size())); ASSERT_TRUE(wrapped_marker_buffer->GetBytes(wrapped_marker_bytes.data(), wrapped_marker_bytes.size())); @@ -496,7 +497,7 @@ // Truncating the array to fewer than 3 bytes should cause IsWrapped() to // return false. ASSERT_LT(3U, wrapped_marker_bytes.size()); - for (size_t i = 0; i < 3; ++i) { + for (wtf_size_t i = 0; i < 3; ++i) { std::unique_ptr<IDBValue> mutant_value = IDBValue::Create( SharedBuffer::Create(wrapped_marker_bytes.data(), i), blob_infos); mutant_value->SetIsolate(scope.GetIsolate()); @@ -507,7 +508,7 @@ // IsWrapped() looks at the first 3 bytes in the value. Flipping any bit in // these 3 bytes should cause IsWrapped() to return false. ASSERT_LT(3U, wrapped_marker_bytes.size()); - for (size_t i = 0; i < 3; ++i) { + for (wtf_size_t i = 0; i < 3; ++i) { for (int j = 0; j < 8; ++j) { char mask = 1 << j; wrapped_marker_bytes[i] ^= mask;
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc index d077611..d19dca27 100644 --- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc +++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -141,7 +141,7 @@ DOMStringList* database_names_list = request_result->DomStringList(); std::unique_ptr<protocol::Array<String>> database_names = protocol::Array<String>::create(); - for (size_t i = 0; i < database_names_list->length(); ++i) + for (uint32_t i = 0; i < database_names_list->length(); ++i) database_names->addItem(database_names_list->item(i)); request_callback_->sendSuccess(std::move(database_names)); } @@ -401,7 +401,7 @@ std::unique_ptr<protocol::Array<String>> array = protocol::Array<String>::create(); const Vector<String>& string_array = idb_key_path.Array(); - for (size_t i = 0; i < string_array.size(); ++i) + for (wtf_size_t i = 0; i < string_array.size(); ++i) array->addItem(string_array[i]); key_path->setArray(std::move(array)); break; @@ -463,7 +463,7 @@ std::unique_ptr<DatabaseWithObjectStores> result = DatabaseWithObjectStores::create() .setName(idb_database->name()) - .setVersion(idb_database->version()) + .setVersion(static_cast<int>(idb_database->version())) .setObjectStores(std::move(object_stores)) .build();
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc index bebb7e6..fe63972a 100644 --- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc +++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
@@ -43,6 +43,7 @@ #include "third_party/blink/renderer/modules/indexeddb/idb_request.h" #include "third_party/blink/renderer/modules/indexeddb/idb_value.h" #include "third_party/blink/renderer/platform/shared_buffer.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" using blink::WebIDBCursor; using blink::WebIDBDatabase; @@ -154,7 +155,7 @@ probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success"); Vector<std::unique_ptr<IDBValue>> idb_values; - idb_values.ReserveInitialCapacity(values.size()); + idb_values.ReserveInitialCapacity(SafeCast<wtf_size_t>(values.size())); for (WebIDBValue& value : values) { std::unique_ptr<IDBValue> idb_value = value.ReleaseIdbValue(); idb_value->SetIsolate(request_->GetIsolate());
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h index d332924c..d6980c28 100644 --- a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h +++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h
@@ -23,7 +23,7 @@ const HashMap<String, String>& Map() const { return layout_map_; } // IDL attributes / methods - size_t size() const { return layout_map_.size(); } + uint32_t size() const { return layout_map_.size(); } void Trace(blink::Visitor* visitor) override { ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc index 503294a..93c78e5 100644 --- a/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc +++ b/third_party/blink/renderer/modules/media_capabilities/media_capabilities.cc
@@ -43,7 +43,7 @@ if (std::isfinite(result)) return result > 0 ? result : std::numeric_limits<double>::quiet_NaN(); - size_t slash_position = fps_str.find('/'); + wtf_size_t slash_position = fps_str.find('/'); if (slash_position == kNotFound) return std::numeric_limits<double>::quiet_NaN();
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc index f11ee55..0f49a4b 100644 --- a/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc +++ b/third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.cc
@@ -377,7 +377,7 @@ // device_orientation_angle snapped to nearest multiple of 90. int device_orientation_angle90 = - std::lround(device_orientation_angle / 90) * 90; + static_cast<int>(std::lround(device_orientation_angle / 90) * 90); // To be considered portrait or landscape, allow the device to be rotated 23 // degrees (chosen to approximately match Android's behavior) to either side
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc index fa721b2..c396e06 100644 --- a/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc +++ b/third_party/blink/renderer/modules/mediacapturefromelement/html_media_element_capture.cc
@@ -84,11 +84,11 @@ : MediaStreamRegistry::Registry().LookupMediaStreamDescriptor( media_element_->currentSrc().GetString()); DCHECK(descriptor); - for (size_t i = 0; i < descriptor->NumberOfAudioComponents(); i++) { + for (unsigned i = 0; i < descriptor->NumberOfAudioComponents(); i++) { media_stream_->AddTrackByComponentAndFireEvents( descriptor->AudioComponent(i)); } - for (size_t i = 0; i < descriptor->NumberOfVideoComponents(); i++) { + for (unsigned i = 0; i < descriptor->NumberOfVideoComponents(); i++) { media_stream_->AddTrackByComponentAndFireEvents( descriptor->VideoComponent(i)); }
diff --git a/third_party/blink/renderer/modules/mediasession/media_metadata.cc b/third_party/blink/renderer/modules/mediasession/media_metadata.cc index 831942c..caaa6a4 100644 --- a/third_party/blink/renderer/modules/mediasession/media_metadata.cc +++ b/third_party/blink/renderer/modules/mediasession/media_metadata.cc
@@ -55,7 +55,7 @@ ScriptState* script_state) const { Vector<v8::Local<v8::Value>> result(artwork_.size()); - for (size_t i = 0; i < artwork_.size(); ++i) { + for (wtf_size_t i = 0; i < artwork_.size(); ++i) { result[i] = FreezeV8Object(ToV8(artwork_[i], script_state), script_state->GetIsolate()); }
diff --git a/third_party/blink/renderer/modules/mediasource/media_source.cc b/third_party/blink/renderer/modules/mediasource/media_source.cc index 1fba87a..2591f34 100644 --- a/third_party/blink/renderer/modules/mediasource/media_source.cc +++ b/third_party/blink/renderer/modules/mediasource/media_source.cc
@@ -269,7 +269,7 @@ active_source_buffers_->Clear(); // Clear SourceBuffer references to this object. - for (unsigned long i = 0; i < source_buffers_->length(); ++i) + for (unsigned i = 0; i < source_buffers_->length(); ++i) source_buffers_->item(i)->RemovedFromMediaSource(); source_buffers_->Clear(); @@ -280,7 +280,7 @@ bool MediaSource::IsUpdating() const { // Return true if any member of |m_sourceBuffers| is updating. - for (unsigned long i = 0; i < source_buffers_->length(); ++i) { + for (unsigned i = 0; i < source_buffers_->length(); ++i) { if (source_buffers_->item(i)->updating()) return true; } @@ -379,7 +379,7 @@ // Implements MediaSource algorithm for HTMLMediaElement.buffered. // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions HeapVector<Member<TimeRanges>> ranges(active_source_buffers_->length()); - for (size_t i = 0; i < active_source_buffers_->length(); ++i) + for (unsigned i = 0; i < active_source_buffers_->length(); ++i) ranges[i] = active_source_buffers_->item(i)->buffered(ASSERT_NO_EXCEPTION); // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges @@ -391,7 +391,7 @@ // SourceBuffer object in activeSourceBuffers. // 3. Let highest end time be the largest range end time in the active ranges. double highest_end_time = -1; - for (size_t i = 0; i < ranges.size(); ++i) { + for (wtf_size_t i = 0; i < ranges.size(); ++i) { unsigned length = ranges[i]->length(); if (length) highest_end_time = std::max( @@ -409,7 +409,7 @@ // 5. For each SourceBuffer object in activeSourceBuffers run the following // steps: bool ended = readyState() == EndedKeyword(); - for (size_t i = 0; i < ranges.size(); ++i) { + for (wtf_size_t i = 0; i < ranges.size(); ++i) { // 5.1 Let source ranges equal the ranges returned by the buffered attribute // on the current SourceBuffer. TimeRanges* source_ranges = ranges[i].Get(); @@ -545,7 +545,7 @@ // media are disallowed. When truncation is necessary, use remove() to // reduce the buffered range before updating duration. double highest_buffered_presentation_timestamp = 0; - for (size_t i = 0; i < source_buffers_->length(); ++i) { + for (unsigned i = 0; i < source_buffers_->length(); ++i) { highest_buffered_presentation_timestamp = std::max(highest_buffered_presentation_timestamp, source_buffers_->item(i)->HighestPresentationTimestamp()); @@ -582,7 +582,7 @@ // Deprecated behavior: if the new duration is less than old duration, // then call remove(new duration, old duration) on all all objects in // sourceBuffers. - for (size_t i = 0; i < source_buffers_->length(); ++i) + for (unsigned i = 0; i < source_buffers_->length(); ++i) source_buffers_->item(i)->remove(new_duration, old_duration, ASSERT_NO_EXCEPTION); } @@ -716,10 +716,10 @@ // SourceBuffer transitions to active are not guaranteed to occur in the // same order as buffers in |m_sourceBuffers|, so this method needs to // insert |sourceBuffer| into |m_activeSourceBuffers|. - size_t index_in_source_buffers = source_buffers_->Find(source_buffer); + wtf_size_t index_in_source_buffers = source_buffers_->Find(source_buffer); DCHECK(index_in_source_buffers != kNotFound); - size_t insert_position = 0; + wtf_size_t insert_position = 0; while (insert_position < active_source_buffers_->length() && source_buffers_->Find(active_source_buffers_->item(insert_position)) < index_in_source_buffers) {
diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer.cc b/third_party/blink/renderer/modules/mediasource/source_buffer.cc index 8007d2c..a495b2e 100644 --- a/third_party/blink/renderer/modules/mediasource/source_buffer.cc +++ b/third_party/blink/renderer/modules/mediasource/source_buffer.cc
@@ -939,7 +939,7 @@ // tracks), then the Track IDs match the ones in the first initialization // segment. if (tracks_match_first_init_segment && new_audio_tracks.size() > 1) { - for (size_t i = 0; i < new_audio_tracks.size(); ++i) { + for (wtf_size_t i = 0; i < new_audio_tracks.size(); ++i) { const String& new_track_id = new_video_tracks[i].id; if (new_track_id != String(audioTracks().AnonymousIndexedGetter(i)->id())) { @@ -950,7 +950,7 @@ } if (tracks_match_first_init_segment && new_video_tracks.size() > 1) { - for (size_t i = 0; i < new_video_tracks.size(); ++i) { + for (wtf_size_t i = 0; i < new_video_tracks.size(); ++i) { const String& new_track_id = new_video_tracks[i].id; if (new_track_id != String(videoTracks().AnonymousIndexedGetter(i)->id())) { @@ -1312,7 +1312,7 @@ // 1. Run the segment parser loop algorithm. // Step 2 doesn't apply since we run Step 1 synchronously here. DCHECK_GE(pending_append_data_.size(), pending_append_data_offset_); - size_t append_size = + wtf_size_t append_size = pending_append_data_.size() - pending_append_data_offset_; // Impose an arbitrary max size for a single append() call so that an append @@ -1320,13 +1320,12 @@ // by looking at YouTube SourceBuffer usage across a variety of bitrates. // This value allows relatively large appends while keeping append() call // duration in the ~5-15ms range. - const size_t kMaxAppendSize = 128 * 1024; + const wtf_size_t kMaxAppendSize = 128 * 1024; if (append_size > kMaxAppendSize) append_size = kMaxAppendSize; TRACE_EVENT_ASYNC_STEP_INTO1("media", "SourceBuffer::appendBuffer", this, - "appending", "appendSize", - static_cast<unsigned>(append_size)); + "appending", "appendSize", append_size); // |zero| is used for 0 byte appends so we always have a valid pointer. // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
diff --git a/third_party/blink/renderer/modules/nfc/nfc.cc b/third_party/blink/renderer/modules/nfc/nfc.cc index c4c5ea0..8584cd83 100644 --- a/third_party/blink/renderer/modules/nfc/nfc.cc +++ b/third_party/blink/renderer/modules/nfc/nfc.cc
@@ -270,7 +270,7 @@ NFCMessagePtr messagePtr = NFCMessage::New(); messagePtr->url = message.url(); messagePtr->data.resize(message.records().size()); - for (size_t i = 0; i < message.records().size(); ++i) { + for (wtf_size_t i = 0; i < message.records().size(); ++i) { NFCRecordPtr record = NFCRecord::From(message.records()[i]); if (record.is_null()) return nullptr; @@ -619,7 +619,7 @@ NFCMessage nfc_message; nfc_message.setURL(message->url); blink::HeapVector<NFCRecord> records; - for (size_t i = 0; i < message->data.size(); ++i) + for (wtf_size_t i = 0; i < message->data.size(); ++i) records.push_back(ToNFCRecord(script_state, message->data[i])); nfc_message.setRecords(records); return nfc_message; @@ -627,7 +627,7 @@ size_t GetNFCMessageSize(const device::mojom::blink::NFCMessagePtr& message) { size_t message_size = message->url.CharactersSizeInBytes(); - for (size_t i = 0; i < message->data.size(); ++i) { + for (wtf_size_t i = 0; i < message->data.size(); ++i) { message_size += message->data[i]->media_type.CharactersSizeInBytes(); message_size += message->data[i]->data.size(); } @@ -775,7 +775,7 @@ } // https://w3c.github.io/web-nfc/#dom-nfc-cancelwatch -ScriptPromise NFC::cancelWatch(ScriptState* script_state, long id) { +ScriptPromise NFC::cancelWatch(ScriptState* script_state, int32_t id) { ScriptPromise promise = RejectIfNotSupported(script_state); if (!promise.IsEmpty()) return promise;
diff --git a/third_party/blink/renderer/modules/nfc/nfc.h b/third_party/blink/renderer/modules/nfc/nfc.h index 39363804..554581d 100644 --- a/third_party/blink/renderer/modules/nfc/nfc.h +++ b/third_party/blink/renderer/modules/nfc/nfc.h
@@ -52,7 +52,7 @@ ScriptPromise watch(ScriptState*, V8MessageCallback*, const NFCWatchOptions&); // Cancels watch operation with id. - ScriptPromise cancelWatch(ScriptState*, long id); + ScriptPromise cancelWatch(ScriptState*, int32_t id); // Cancels all watch operations. ScriptPromise cancelWatch(ScriptState*);
diff --git a/third_party/blink/renderer/modules/notifications/notification.cc b/third_party/blink/renderer/modules/notifications/notification.cc index 41afb93..99112d7 100644 --- a/third_party/blink/renderer/modules/notifications/notification.cc +++ b/third_party/blink/renderer/modules/notifications/notification.cc
@@ -57,6 +57,7 @@ #include "third_party/blink/renderer/platform/runtime_enabled_features.h" #include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" namespace blink { @@ -346,7 +347,7 @@ const Vector<mojom::blink::NotificationActionPtr>& actions = data_->actions.value(); result.Grow(actions.size()); - for (size_t i = 0; i < actions.size(); ++i) { + for (wtf_size_t i = 0; i < actions.size(); ++i) { NotificationAction action; switch (actions[i]->type) {
diff --git a/third_party/blink/renderer/modules/notifications/notification_data.cc b/third_party/blink/renderer/modules/notifications/notification_data.cc index 7667504..4e22ec98 100644 --- a/third_party/blink/renderer/modules/notifications/notification_data.cc +++ b/third_party/blink/renderer/modules/notifications/notification_data.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/modules/notifications/notification_options.h" #include "third_party/blink/renderer/modules/vibration/vibration_controller.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" +#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" #include "third_party/blink/renderer/platform/wtf/time.h" @@ -100,7 +101,7 @@ notification_data->data = Vector<uint8_t>(); notification_data->data->Append( serialized_script_value->Data(), - serialized_script_value->DataLengthInBytes()); + SafeCast<wtf_size_t>(serialized_script_value->DataLengthInBytes())); } Vector<mojom::blink::NotificationActionPtr> actions;
diff --git a/third_party/blink/renderer/modules/notifications/notification_data_test.cc b/third_party/blink/renderer/modules/notifications/notification_data_test.cc index edd5925..24296339e 100644 --- a/third_party/blink/renderer/modules/notifications/notification_data_test.cc +++ b/third_party/blink/renderer/modules/notifications/notification_data_test.cc
@@ -134,7 +134,7 @@ ASSERT_EQ(vibration_pattern.size(), notification_data->vibration_pattern->size()); - for (size_t i = 0; i < vibration_pattern.size(); ++i) { + for (wtf_size_t i = 0; i < vibration_pattern.size(); ++i) { EXPECT_EQ( vibration_pattern[i], static_cast<unsigned>(notification_data->vibration_pattern.value()[i])); @@ -259,7 +259,7 @@ ASSERT_EQ(normalized_pattern.size(), notification_data->vibration_pattern->size()); - for (size_t i = 0; i < normalized_pattern.size(); ++i) { + for (wtf_size_t i = 0; i < normalized_pattern.size(); ++i) { EXPECT_EQ(normalized_pattern[i], notification_data->vibration_pattern.value()[i]); } @@ -323,7 +323,7 @@ // The stored actions will be capped to |maxActions| entries. ASSERT_EQ(Notification::maxActions(), notification_data->actions->size()); - for (size_t i = 0; i < Notification::maxActions(); ++i) { + for (wtf_size_t i = 0; i < Notification::maxActions(); ++i) { String expected_action = String::Number(i); EXPECT_EQ(expected_action, notification_data->actions.value()[i]->action); }
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc index cbdac9c6..a4ea869 100644 --- a/third_party/blink/renderer/modules/payments/payment_request.cc +++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -739,17 +739,22 @@ } } -bool AllowedToUsePaymentRequest(const Frame* frame) { +bool AllowedToUsePaymentRequest(const ExecutionContext* execution_context) { // To determine whether a Document object |document| is allowed to use the // feature indicated by attribute name |allowpaymentrequest|, run these steps: + // If this context is not a document, return false. + if (!execution_context->IsDocument()) + return false; + // 1. If |document| has no browsing context, then return false. - if (!frame) + const Document* document = ToDocument(execution_context); + if (!document->GetFrame()) return false; // 2. If Feature Policy is enabled, return the policy for "payment" feature. - return frame->DeprecatedIsFeatureEnabled( - mojom::FeaturePolicyFeature::kPayment, ReportOptions::kReportOnFailure); + return document->IsFeatureEnabled(mojom::FeaturePolicyFeature::kPayment, + ReportOptions::kReportOnFailure); } void WarnIgnoringQueryQuotaForCanMakePayment( @@ -1056,7 +1061,7 @@ &PaymentRequest::OnCompleteTimeout) { DCHECK(GetExecutionContext()->IsSecureContext()); - if (!AllowedToUsePaymentRequest(GetFrame())) { + if (!AllowedToUsePaymentRequest(execution_context)) { exception_state.ThrowSecurityError( "Must be in a top-level browsing context or an iframe needs to specify " "'allowpaymentrequest' explicitly");
diff --git a/third_party/blink/renderer/modules/webusb/usb.cc b/third_party/blink/renderer/modules/webusb/usb.cc index 1a65f8e..da13f98d 100644 --- a/third_party/blink/renderer/modules/webusb/usb.cc +++ b/third_party/blink/renderer/modules/webusb/usb.cc
@@ -114,9 +114,7 @@ ExecutionContext* execution_context = ExecutionContext::From(script_state); if (execution_context && execution_context->IsDocument()) { ToDocument(execution_context) - ->GetFrame() - ->DeprecatedReportFeaturePolicyViolation( - mojom::FeaturePolicyFeature::kUsb); + ->ReportFeaturePolicyViolation(mojom::FeaturePolicyFeature::kUsb); } return ScriptPromise::RejectWithDOMException( script_state, DOMException::Create(DOMExceptionCode::kSecurityError, @@ -134,14 +132,14 @@ ScriptPromise USB::requestDevice(ScriptState* script_state, const USBDeviceRequestOptions& options) { LocalFrame* frame = GetFrame(); - if (!frame) { + if (!frame || !frame->GetDocument()) { return ScriptPromise::RejectWithDOMException( script_state, DOMException::Create(DOMExceptionCode::kNotSupportedError)); } - if (!frame->DeprecatedIsFeatureEnabled(mojom::FeaturePolicyFeature::kUsb, - ReportOptions::kReportOnFailure)) { + if (!frame->GetDocument()->IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUsb, ReportOptions::kReportOnFailure)) { return ScriptPromise::RejectWithDOMException( script_state, DOMException::Create(DOMExceptionCode::kSecurityError, kFeaturePolicyBlocked)); @@ -315,9 +313,8 @@ } bool USB::IsFeatureEnabled() const { - ExecutionContext* context = GetExecutionContext(); - FeaturePolicy* policy = context->GetSecurityContext().GetFeaturePolicy(); - return policy->IsFeatureEnabled(mojom::FeaturePolicyFeature::kUsb); + return GetExecutionContext()->GetSecurityContext().IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUsb); } void USB::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index 6f92724..bfbb5e6 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -136,10 +136,9 @@ effect_node.has_render_surface = true; root_layer_->SetEffectTreeIndex(effect_node.id); - current_effect_id_ = effect_node.id; - current_effect_type_ = CcEffectType::kEffect; - current_effect_ = &EffectPaintPropertyNode::Root(); - current_clip_ = current_effect_->OutputClip(); + SetCurrentEffectState(effect_node, CcEffectType::kEffect, + &EffectPaintPropertyNode::Root(), + &ClipPaintPropertyNode::Root()); } void PropertyTreeManager::SetupRootScrollNode() { @@ -155,6 +154,26 @@ root_layer_->SetScrollTreeIndex(scroll_node.id); } +void PropertyTreeManager::SetCurrentEffectState( + const cc::EffectNode& cc_effect_node, + CcEffectType effect_type, + const EffectPaintPropertyNode* effect, + const ClipPaintPropertyNode* clip) { + current_.effect_id = cc_effect_node.id; + current_.effect_type = effect_type; + current_.effect = effect; + current_.clip = clip; + if (cc_effect_node.has_render_surface) + current_.render_surface_transform = effect->LocalTransformSpace(); +} + +// TODO(crbug.com/504464): Remove this when move render surface decision logic +// into cc compositor thread. +void PropertyTreeManager::SetCurrentEffectHasRenderSurface() { + GetEffectTree().Node(current_.effect_id)->has_render_surface = true; + current_.render_surface_transform = current_.effect->LocalTransformSpace(); +} + int PropertyTreeManager::EnsureCompositorTransformNode( const TransformPaintPropertyNode* transform_node) { DCHECK(transform_node); @@ -255,14 +274,15 @@ // If the parent transform node flattens transform (as |transform_node| // flattens inherited transform) while it participates in the 3d sorting // context of an ancestor, cc needs a render surface for correct flattening. - if (transform_node->FlattensInheritedTransform() && + // TODO(crbug.com/504464): Move the logic into cc compositor thread. + auto* current_cc_effect = GetEffectTree().Node(current_.effect_id); + if (current_cc_effect && !current_cc_effect->has_render_surface && + current_cc_effect->transform_id == parent_id && + transform_node->FlattensInheritedTransform() && transform_node->Parent() && transform_node->Parent()->RenderingContextId() && - !transform_node->Parent()->FlattensInheritedTransform()) { - auto* current_cc_effect = GetEffectTree().Node(current_effect_id_); - if (current_cc_effect && current_cc_effect->transform_id == parent_id) - current_cc_effect->has_render_surface = true; - } + !transform_node->Parent()->FlattensInheritedTransform()) + SetCurrentEffectHasRenderSurface(); auto result = transform_node_map_.Set(transform_node, id); DCHECK(result.is_new_entry); @@ -379,12 +399,12 @@ } void PropertyTreeManager::EmitClipMaskLayer() { - int clip_id = EnsureCompositorClipNode(current_clip_); + int clip_id = EnsureCompositorClipNode(current_.clip); CompositorElementId mask_isolation_id, mask_effect_id; cc::Layer* mask_layer = client_.CreateOrReuseSynthesizedClipLayer( - current_clip_, mask_isolation_id, mask_effect_id); + current_.clip, mask_isolation_id, mask_effect_id); - cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_effect_id_); + cc::EffectNode& mask_isolation = *GetEffectTree().Node(current_.effect_id); // Assignment of mask_isolation.stable_id was delayed until now. // See PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded(). DCHECK_EQ(static_cast<uint64_t>(cc::EffectNode::INVALID_STABLE_ID), @@ -392,13 +412,13 @@ mask_isolation.stable_id = mask_isolation_id.GetInternalValue(); cc::EffectNode& mask_effect = *GetEffectTree().Node( - GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); + GetEffectTree().Insert(cc::EffectNode(), current_.effect_id)); mask_effect.stable_id = mask_effect_id.GetInternalValue(); mask_effect.clip_id = clip_id; mask_effect.has_render_surface = true; mask_effect.blend_mode = SkBlendMode::kDstIn; - const auto* clip_space = current_clip_->LocalTransformSpace(); + const auto* clip_space = current_.clip->LocalTransformSpace(); layer_list_builder_->Add(mask_layer); mask_layer->set_property_tree_sequence_number( root_layer_->property_tree_sequence_number()); @@ -415,7 +435,7 @@ void PropertyTreeManager::CloseCcEffect() { DCHECK(effect_stack_.size()); - const EffectStackEntry& previous_state = effect_stack_.back(); + const auto& previous_state = effect_stack_.back(); // An effect with exotic blending that is masked by a synthesized clip must // have its blending to the outermost synthesized clip. It is because @@ -425,18 +445,15 @@ // thus the clip can't be shared with sibling layers, and must be closed now. bool clear_synthetic_effects = !IsCurrentCcEffectSynthetic() && - current_effect_->BlendMode() != SkBlendMode::kSrcOver; + current_.effect->BlendMode() != SkBlendMode::kSrcOver; // We are about to close an effect that was synthesized for isolating // a clip mask. Now emit the actual clip mask that will be composited on // top of masked contents with SkBlendMode::kDstIn. - if (IsCurrentCcEffectSynthetic()) + if (IsCurrentCcEffectSyntheticForNonTrivialClip()) EmitClipMaskLayer(); - current_effect_id_ = previous_state.effect_id; - current_effect_type_ = previous_state.effect_type; - current_effect_ = previous_state.effect; - current_clip_ = previous_state.clip; + current_ = previous_state; effect_stack_.pop_back(); if (clear_synthetic_effects) { @@ -445,6 +462,12 @@ } } +static bool TransformsAre2dAxisAligned(const TransformPaintPropertyNode* a, + const TransformPaintPropertyNode* b) { + return a == b || GeometryMapper::SourceToDestinationProjection(a, b) + .Preserves2dAxisAlignment(); +} + int PropertyTreeManager::SwitchToEffectNodeWithSynthesizedClip( const EffectPaintPropertyNode& next_effect, const ClipPaintPropertyNode& next_clip) { @@ -460,7 +483,7 @@ // For example with the following clip and effect tree and pending layers: // E0 <-- E1 // C0 <-- C1(rounded) - // [P0(E1,C0), P1(E1,C1), P2(E0, C1)] + // [P0(E1,C0), P1(E1,C1), P2(E0,C1)] // In effect stack diagram: // P0(C0) P1(C1) // [ E1 ] P2(C1) @@ -470,11 +493,11 @@ // E0 <+- E1 <-- E_C1_1 <-- E_C1_1M // +- E_C1_2 <-- E_C1_2M // C0 <-- C1 - // [L0(E1,C0), L1(E_C1_1, C1), L_C1_1(E_C1_1M, C1), L2(E0, C1), - // L_C1_2(E_C1_2M, C1)] + // [L0(E1,C0), L1(E_C1_1, C1), L1M(E_C1_1M, C1), L2(E_C1_2, C1), + // L2M(E_C1_2M, C1)] // In effect stack diagram: - // L_C1_1 - // L1(C1) [ E_C1_1M ] L_C2_2 + // L1M(C1) + // L1(C1) [ E_C1_1M ] L2M(C1) // L0(C0) [ E_C1_1 ] L2(C1) [ E_C1_2M ] // [ E1 ][ E_C1_2 ] // [ E0 ] @@ -488,23 +511,22 @@ // emits P1. // Prior to emitting P2, this method is invoked with (E0, C1). Both previously // entered effects must be closed, because synthetic effect for C1 is enclosed - // by E1, thus must be closed before E1 can be closed. A mask layer L_C1_1 - // is generated along with an internal effect node for blending. After closing + // by E1, thus must be closed before E1 can be closed. A mask layer L1M is + // generated along with an internal effect node for blending. After closing // both effects, C1 has to be entered again, thus generates another synthetic // compositor effect. The caller emits P2. // At last, the caller invokes Finalize() to close the unclosed synthetic - // effect. Another mask layer L_C1_2 is generated, along with its internal + // effect. Another mask layer L2M is generated, along with its internal // effect node for blending. const auto& ancestor = - *LowestCommonAncestor(*current_effect_, next_effect).Unalias(); - while (current_effect_ != &ancestor) + *LowestCommonAncestor(*current_.effect, next_effect).Unalias(); + while (current_.effect != &ancestor) CloseCcEffect(); bool newly_built = BuildEffectNodesRecursively(&next_effect); SynthesizeCcEffectsForClipsIfNeeded(&next_clip, SkBlendMode::kSrcOver, newly_built); - - return current_effect_id_; + return current_.effect_id; } static bool IsNodeOnAncestorChain(const ClipPaintPropertyNode& find, @@ -523,34 +545,48 @@ return false; } +base::Optional<PropertyTreeManager::CcEffectType> +PropertyTreeManager::NeedsSyntheticEffect( + const ClipPaintPropertyNode& clip) const { + if (clip.ClipRect().IsRounded() || clip.ClipPath()) + return CcEffectType::kSyntheticForNonTrivialClip; + + // Cc requires that a rectangluar clip is 2d-axis-aligned with the render + // surface to correctly apply the clip. + if (!TransformsAre2dAxisAligned(clip.LocalTransformSpace(), + current_.render_surface_transform)) + return CcEffectType::kSyntheticFor2dAxisAlignment; + + return base::nullopt; +} + SkBlendMode PropertyTreeManager::SynthesizeCcEffectsForClipsIfNeeded( const ClipPaintPropertyNode* target_clip, SkBlendMode delegated_blend, bool effect_is_newly_built) { target_clip = target_clip->Unalias(); if (delegated_blend != SkBlendMode::kSrcOver) { - // Exit all synthetic effect node for rounded clip if the next child has - // exotic blending mode because it has to access the backdrop of enclosing - // effect. + // Exit all synthetic effect node if the next child has exotic blending mode + // because it has to access the backdrop of enclosing effect. while (IsCurrentCcEffectSynthetic()) CloseCcEffect(); // An effect node can't omit render surface if it has child with exotic // blending mode. See comments below for more detail. // TODO(crbug.com/504464): Remove premature optimization here. - GetEffectTree().Node(current_effect_id_)->has_render_surface = true; + SetCurrentEffectHasRenderSurface(); } else { // Exit synthetic effects until there are no more synthesized clips below // our lowest common ancestor. const auto& lca = - *LowestCommonAncestor(*current_clip_, *target_clip).Unalias(); - while (current_clip_ != &lca) { + *LowestCommonAncestor(*current_.clip, *target_clip).Unalias(); + while (current_.clip != &lca) { DCHECK(IsCurrentCcEffectSynthetic()); - const auto* pre_exit_clip = current_clip_; + const auto* pre_exit_clip = current_.clip; CloseCcEffect(); // We may run past the lowest common ancestor because it may not have // been synthesized. - if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_clip_)) + if (IsNodeOnAncestorChain(lca, *pre_exit_clip, *current_.clip)) break; } @@ -561,48 +597,64 @@ // See comments in PropertyTreeManager::BuildEffectNodesRecursively(). // TODO(crbug.com/504464): Remove premature optimization here. if (!effect_is_newly_built && !IsCurrentCcEffectSynthetic() && - current_effect_->Opacity() != 1.f) - GetEffectTree().Node(current_effect_id_)->has_render_surface = true; + current_.effect->Opacity() != 1.f) + SetCurrentEffectHasRenderSurface(); } - DCHECK(current_clip_->IsAncestorOf(*target_clip)); + DCHECK(current_.clip->IsAncestorOf(*target_clip)); - Vector<const ClipPaintPropertyNode*> pending_clips; - for (; target_clip != current_clip_; + struct PendingClip { + const ClipPaintPropertyNode* clip; + CcEffectType type; + }; + Vector<PendingClip> pending_clips; + for (; target_clip != current_.clip; target_clip = target_clip->Parent()->Unalias()) { DCHECK(target_clip); - bool should_synthesize = - target_clip->ClipRect().IsRounded() || target_clip->ClipPath(); - if (should_synthesize) - pending_clips.push_back(target_clip); + if (auto type = NeedsSyntheticEffect(*target_clip)) + pending_clips.emplace_back(PendingClip{target_clip, *type}); } for (size_t i = pending_clips.size(); i--;) { - const ClipPaintPropertyNode* next_clip = pending_clips[i]; + const auto& pending_clip = pending_clips[i]; - // For each of clip synthesized, an isolation effect node needs to be - // created to enclose only the layers that should be masked by the clip. - cc::EffectNode& mask_isolation = *GetEffectTree().Node( - GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); - // mask_isolation.stable_id will be assigned later when the effect is - // closed. For now the default value of INVALID_STABLE_ID is used. - // See PropertyTreeManager::EmitClipMaskLayer(). - mask_isolation.clip_id = EnsureCompositorClipNode(next_clip); - mask_isolation.has_render_surface = true; + // For a non-trivial clip, the synthetic effect is an isolation to enclose + // only the layers that should be masked by the synthesized clip. + // For a non-2d-axis-preserving clip, the synthetic effect creates a render + // surface which is axis-aligned with the clip. + cc::EffectNode& synthetic_effect = *GetEffectTree().Node( + GetEffectTree().Insert(cc::EffectNode(), current_.effect_id)); + if (pending_clip.type == CcEffectType::kSyntheticForNonTrivialClip) { + synthetic_effect.clip_id = EnsureCompositorClipNode(pending_clip.clip); + // For non-trivial clip, isolation_effect.stable_id will be assigned later + // when the effect is closed. For now the default value INVALID_STABLE_ID + // is used. See PropertyTreeManager::EmitClipMaskLayer(). + } else { + DCHECK_EQ(pending_clip.type, CcEffectType::kSyntheticFor2dAxisAlignment); + synthetic_effect.stable_id = + CompositorElementIdFromUniqueObjectId(NewUniqueObjectId()) + .GetInternalValue(); + // The clip of the synthetic effect is the parent of the clip, so that + // the clip itself will be applied in the render surface. + synthetic_effect.clip_id = + EnsureCompositorClipNode(pending_clip.clip->Parent()); + } + synthetic_effect.transform_id = + EnsureCompositorTransformNode(pending_clip.clip->LocalTransformSpace()); + synthetic_effect.has_render_surface = true; // Clip and kDstIn do not commute. This shall never be reached because // kDstIn is only used internally to implement CSS clip-path and mask, // and there is never a difference between the output clip of the effect // and the mask content. DCHECK(delegated_blend != SkBlendMode::kDstIn); - mask_isolation.blend_mode = delegated_blend; + synthetic_effect.blend_mode = delegated_blend; delegated_blend = SkBlendMode::kSrcOver; - effect_stack_.emplace_back( - EffectStackEntry{current_effect_id_, current_effect_type_, - current_effect_, current_clip_}); - current_effect_id_ = mask_isolation.id; - current_effect_type_ = CcEffectType::kSynthesizedClip; - current_clip_ = next_clip; + effect_stack_.emplace_back(current_); + SetCurrentEffectState(synthetic_effect, pending_clip.type, current_.effect, + pending_clip.clip); + current_.render_surface_transform = + pending_clip.clip->LocalTransformSpace(); } return delegated_blend; @@ -611,12 +663,12 @@ bool PropertyTreeManager::BuildEffectNodesRecursively( const EffectPaintPropertyNode* next_effect) { next_effect = next_effect ? next_effect->Unalias() : nullptr; - if (next_effect == current_effect_) + if (next_effect == current_.effect) return false; DCHECK(next_effect); bool newly_built = BuildEffectNodesRecursively(next_effect->Parent()); - DCHECK_EQ(next_effect->Parent()->Unalias(), current_effect_); + DCHECK_EQ(next_effect->Parent()->Unalias(), current_.effect); #if DCHECK_IS_ON() DCHECK(!effect_nodes_converted_.Contains(next_effect)) @@ -627,10 +679,11 @@ SkBlendMode used_blend_mode; int output_clip_id; - if (next_effect->OutputClip()) { + const auto* output_clip = next_effect->OutputClip(); + if (output_clip) { used_blend_mode = SynthesizeCcEffectsForClipsIfNeeded( - next_effect->OutputClip(), next_effect->BlendMode(), newly_built); - output_clip_id = EnsureCompositorClipNode(next_effect->OutputClip()); + output_clip, next_effect->BlendMode(), newly_built); + output_clip_id = EnsureCompositorClipNode(output_clip); } else { while (IsCurrentCcEffectSynthetic()) CloseCcEffect(); @@ -638,15 +691,17 @@ // blending mode, nor being opacity-only node with more than one child. // TODO(crbug.com/504464): Remove premature optimization here. if (next_effect->BlendMode() != SkBlendMode::kSrcOver || - (!newly_built && current_effect_->Opacity() != 1.f)) - GetEffectTree().Node(current_effect_id_)->has_render_surface = true; + (!newly_built && current_.effect->Opacity() != 1.f)) + SetCurrentEffectHasRenderSurface(); used_blend_mode = next_effect->BlendMode(); - output_clip_id = GetEffectTree().Node(current_effect_id_)->clip_id; + output_clip = current_.clip; + output_clip_id = GetEffectTree().Node(current_.effect_id)->clip_id; + DCHECK_EQ(output_clip_id, EnsureCompositorClipNode(output_clip)); } cc::EffectNode& effect_node = *GetEffectTree().Node( - GetEffectTree().Insert(cc::EffectNode(), current_effect_id_)); + GetEffectTree().Insert(cc::EffectNode(), current_.effect_id)); effect_node.stable_id = next_effect->GetCompositorElementId().GetInternalValue(); effect_node.clip_id = output_clip_id; @@ -663,6 +718,7 @@ if (!next_effect->Filter().IsEmpty() || used_blend_mode != SkBlendMode::kSrcOver) effect_node.has_render_surface = true; + effect_node.opacity = next_effect->Opacity(); if (next_effect->GetColorFilter() != kColorFilterNone) { // Currently color filter is only used by SVG masks. @@ -691,14 +747,9 @@ property_trees_.element_id_to_effect_node_index[compositor_element_id] = effect_node.id; } - effect_stack_.emplace_back(EffectStackEntry{current_effect_id_, - current_effect_type_, - current_effect_, current_clip_}); - current_effect_id_ = effect_node.id; - current_effect_type_ = CcEffectType::kEffect; - current_effect_ = next_effect; - if (next_effect->OutputClip()) - current_clip_ = next_effect->OutputClip()->Unalias(); + effect_stack_.emplace_back(current_); + SetCurrentEffectState(effect_node, CcEffectType::kEffect, next_effect, + output_clip); return true; }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h index ee0fc81..edfce2ee 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -19,6 +19,7 @@ class PropertyTrees; class ScrollTree; class TransformTree; +struct EffectNode; struct TransformNode; } @@ -126,9 +127,38 @@ bool effect_is_newly_built); void EmitClipMaskLayer(); void CloseCcEffect(); + bool IsCurrentCcEffectSynthetic() const { - return current_effect_type_ != CcEffectType::kEffect; + return current_.effect_type != CcEffectType::kEffect; } + bool IsCurrentCcEffectSyntheticForNonTrivialClip() const { + return current_.effect_type == CcEffectType::kSyntheticForNonTrivialClip; + } + + // The type of operation the current cc effect node applies. + enum class CcEffectType { + // The cc effect corresponds to a Blink effect node. + kEffect, + // The cc effect is synthetic for a blink clip node that has to be + // rasterized because the clip is non-trivial. + kSyntheticForNonTrivialClip, + // The cc effect is synthetic to create a render surface that is + // 2d-axis-aligned with a blink clip node that is non-2d-axis-aligned + // in the the original render surface. Cc requires a rectangular clip to be + // 2d-axis-aligned with the render surface to correctly apply the clip. + // TODO(crbug.com/504464): This will be changed when we move render surface + // decision logic into the cc compositor thread. + kSyntheticFor2dAxisAlignment, + }; + + base::Optional<CcEffectType> NeedsSyntheticEffect( + const ClipPaintPropertyNode&) const; + + void SetCurrentEffectState(const cc::EffectNode&, + CcEffectType, + const EffectPaintPropertyNode*, + const ClipPaintPropertyNode*); + void SetCurrentEffectHasRenderSurface(); cc::TransformTree& GetTransformTree(); cc::ClipTree& GetClipTree(); @@ -158,32 +188,36 @@ HashMap<const ClipPaintPropertyNode*, int> clip_node_map_; HashMap<const ScrollPaintPropertyNode*, int> scroll_node_map_; - // The cc effect node that has the corresponding drawing state to the - // effect and clip state from the last SwitchToEffectNodeWithSynthesizedClip. - int current_effect_id_; - // The type of operation the current cc effect node applies. kEffect means - // it corresponds to a Blink effect node. kSynthesizedClip means it implements - // a Blink clip node that has to be rasterized. - enum class CcEffectType { kEffect, kSynthesizedClip } current_effect_type_; - // The effect state of the current cc effect node. - const EffectPaintPropertyNode* current_effect_; - // The clip state of the current cc effect node. This value may be shallower - // than the one passed into SwitchToEffectNodeWithSynthesizedClip because not - // every clip needs to be synthesized as cc effect. - // Is set to output clip of the effect if the type is kEffect, or set to the - // synthesized clip node if the type is kSynthesizedClip. - const ClipPaintPropertyNode* current_clip_; + struct EffectState { + // The cc effect node that has the corresponding drawing state to the + // effect and clip state from the last + // SwitchToEffectNodeWithSynthesizedClip. + int effect_id; + CcEffectType effect_type; + // The effect state of the cc effect node. + const EffectPaintPropertyNode* effect; + // The clip state of the cc effect node. This value may be shallower than + // the one passed into SwitchToEffectNodeWithSynthesizedClip because not + // every clip needs to be synthesized as cc effect. + // Is set to output clip of the effect if the type is kEffect, or set to the + // synthesized clip node if the type is kSyntheticForNonTrivialClip. + const ClipPaintPropertyNode* clip; + // The transform space of the containing render surface. + // TODO(crbug.com/504464): Remove this when move render surface decision + // logic into cc compositor thread. + const TransformPaintPropertyNode* render_surface_transform; + }; + + // The current effect state. Virtually it's the top of the effect stack if + // it and effect_stack_ are treated as a whole stack. + EffectState current_; + // This keep track of cc effect stack. Whenever a new cc effect is nested, // a new entry is pushed, and the entry will be popped when the effect closed. // Note: This is a "restore stack", i.e. the top element does not represent - // the current state, but the state prior to most recent push. - struct EffectStackEntry { - int effect_id; - CcEffectType effect_type; - const EffectPaintPropertyNode* effect; - const ClipPaintPropertyNode* clip; - }; - Vector<EffectStackEntry> effect_stack_; + // the current state (which is in current_), but the state prior to most + // recent push. + Vector<EffectState> effect_stack_; #if DCHECK_IS_ON() HashSet<const EffectPaintPropertyNode*> effect_nodes_converted_;
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc index 23ded0e..e14c893 100644 --- a/third_party/blink/renderer/platform/transforms/transformation_matrix.cc +++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.cc
@@ -1827,6 +1827,56 @@ return true; } +// This is the same as gfx::Transform::Preserves2dAxisAlignment(). +bool TransformationMatrix::Preserves2dAxisAlignment() const { + // Check whether an axis aligned 2-dimensional rect would remain axis-aligned + // after being transformed by this matrix (and implicitly projected by + // dropping any non-zero z-values). + // + // The 4th column can be ignored because translations don't affect axis + // alignment. The 3rd column can be ignored because we are assuming 2d + // inputs, where z-values will be zero. The 3rd row can also be ignored + // because we are assuming 2d outputs, and any resulting z-value is dropped + // anyway. For the inner 2x2 portion, the only effects that keep a rect axis + // aligned are (1) swapping axes and (2) scaling axes. This can be checked by + // verifying only 1 element of every column and row is non-zero. Degenerate + // cases that project the x or y dimension to zero are considered to preserve + // axis alignment. + // + // If the matrix does have perspective component that is affected by x or y + // values: The current implementation conservatively assumes that axis + // alignment is not preserved. + bool has_x_or_y_perspective = M14() != 0 || M24() != 0; + if (has_x_or_y_perspective) + return false; + + constexpr double kEpsilon = std::numeric_limits<double>::epsilon(); + + int num_non_zero_in_row_1 = 0; + int num_non_zero_in_row_2 = 0; + int num_non_zero_in_col_1 = 0; + int num_non_zero_in_col_2 = 0; + if (std::abs(M11()) > kEpsilon) { + num_non_zero_in_col_1++; + num_non_zero_in_row_1++; + } + if (std::abs(M12()) > kEpsilon) { + num_non_zero_in_col_1++; + num_non_zero_in_row_2++; + } + if (std::abs(M21()) > kEpsilon) { + num_non_zero_in_col_2++; + num_non_zero_in_row_1++; + } + if (std::abs(M22()) > kEpsilon) { + num_non_zero_in_col_2++; + num_non_zero_in_row_2++; + } + + return num_non_zero_in_row_1 <= 1 && num_non_zero_in_row_2 <= 1 && + num_non_zero_in_col_1 <= 1 && num_non_zero_in_col_2 <= 1; +} + FloatSize TransformationMatrix::To2DTranslation() const { DCHECK(IsIdentityOr2DTranslation()); return FloatSize(matrix_[3][0], matrix_[3][1]);
diff --git a/third_party/blink/renderer/platform/transforms/transformation_matrix.h b/third_party/blink/renderer/platform/transforms/transformation_matrix.h index 8d88d5d..e943e58 100644 --- a/third_party/blink/renderer/platform/transforms/transformation_matrix.h +++ b/third_party/blink/renderer/platform/transforms/transformation_matrix.h
@@ -474,6 +474,10 @@ bool IsIntegerTranslation() const; + // Returns true if axis-aligned 2d rects will remain axis-aligned after being + // transformed by this matrix. + bool Preserves2dAxisAlignment() const; + // If this transformation is identity or 2D translation, returns the // translation. FloatSize To2DTranslation() const;
diff --git a/third_party/blink/tools/blinkpy/w3c/chromium_commit.py b/third_party/blink/tools/blinkpy/w3c/chromium_commit.py index 2632639..f0282d0 100644 --- a/third_party/blink/tools/blinkpy/w3c/chromium_commit.py +++ b/third_party/blink/tools/blinkpy/w3c/chromium_commit.py
@@ -86,7 +86,7 @@ def author(self): return self.host.executive.run_command([ - 'git', 'show', '--format="%aN <%aE>"', '--no-patch', self.sha + 'git', 'show', '--format=%aN <%aE>', '--no-patch', self.sha ], cwd=self.absolute_chromium_dir).strip() def message(self):
diff --git a/third_party/blink/tools/blinkpy/w3c/common.py b/third_party/blink/tools/blinkpy/w3c/common.py index a5cd9b1..a0f8932 100644 --- a/third_party/blink/tools/blinkpy/w3c/common.py +++ b/third_party/blink/tools/blinkpy/w3c/common.py
@@ -17,6 +17,11 @@ EXPORT_PR_LABEL = 'chromium-export' PROVISIONAL_PR_LABEL = 'do not merge yet' +# These are only set in a new WPT checkout, and they should be consistent with +# the bot's GitHub account (chromium-wpt-export-bot). +DEFAULT_WPT_COMMITTER_NAME = 'Chromium WPT Sync' +DEFAULT_WPT_COMMITTER_EMAIL = 'blink-w3c-test-autoroller@chromium.org' + # TODO(qyearsley): Avoid hard-coding third_party/WebKit/LayoutTests. CHROMIUM_WPT_DIR = 'third_party/WebKit/LayoutTests/external/wpt/'
diff --git a/third_party/blink/tools/blinkpy/w3c/local_wpt.py b/third_party/blink/tools/blinkpy/w3c/local_wpt.py index 240a0de..4ab3b9e 100644 --- a/third_party/blink/tools/blinkpy/w3c/local_wpt.py +++ b/third_party/blink/tools/blinkpy/w3c/local_wpt.py
@@ -7,7 +7,13 @@ import logging from blinkpy.common.system.executive import ScriptError -from blinkpy.w3c.common import WPT_GH_SSH_URL_TEMPLATE, WPT_MIRROR_URL, CHROMIUM_WPT_DIR +from blinkpy.w3c.common import ( + CHROMIUM_WPT_DIR, + DEFAULT_WPT_COMMITTER_EMAIL, + DEFAULT_WPT_COMMITTER_NAME, + WPT_GH_SSH_URL_TEMPLATE, + WPT_MIRROR_URL, +) _log = logging.getLogger(__name__) @@ -42,8 +48,13 @@ remote_url = WPT_MIRROR_URL _log.info('No credentials given, using wpt mirror URL.') _log.info('It is possible for the mirror to be delayed; see https://crbug.com/698272.') + # Do not use self.run here because self.path doesn't exist yet. self.host.executive.run_command(['git', 'clone', remote_url, self.path]) + _log.info('Setting git user name & email in %s', self.path) + self.run(['git', 'config', 'user.name', DEFAULT_WPT_COMMITTER_NAME]) + self.run(['git', 'config', 'user.email', DEFAULT_WPT_COMMITTER_EMAIL]) + def run(self, command, **kwargs): """Runs a command in the local WPT directory.""" # TODO(robertma): Migrate to blinkpy.common.checkout.Git. (crbug.com/676399)
diff --git a/third_party/blink/tools/blinkpy/w3c/local_wpt_unittest.py b/third_party/blink/tools/blinkpy/w3c/local_wpt_unittest.py index beadebde..e99dbb2 100644 --- a/third_party/blink/tools/blinkpy/w3c/local_wpt_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/local_wpt_unittest.py
@@ -8,6 +8,7 @@ from blinkpy.common.system.executive import ScriptError from blinkpy.common.system.executive_mock import MockExecutive, mock_git_commands from blinkpy.common.system.filesystem_mock import MockFileSystem +from blinkpy.w3c.common import DEFAULT_WPT_COMMITTER_EMAIL, DEFAULT_WPT_COMMITTER_NAME from blinkpy.w3c.local_wpt import LocalWPT @@ -34,6 +35,8 @@ self.assertEqual(host.executive.calls, [ ['git', 'clone', 'https://token@github.com/web-platform-tests/wpt.git', '/tmp/wpt'], + ['git', 'config', 'user.name', DEFAULT_WPT_COMMITTER_NAME], + ['git', 'config', 'user.email', DEFAULT_WPT_COMMITTER_EMAIL], ]) def test_constructor(self): @@ -55,6 +58,8 @@ local_wpt.create_branch_with_patch('chromium-export-decafbad', 'message', 'patch', 'author <author@author.com>') self.assertEqual(host.executive.calls, [ ['git', 'clone', 'https://token@github.com/web-platform-tests/wpt.git', '/tmp/wpt'], + ['git', 'config', 'user.name', DEFAULT_WPT_COMMITTER_NAME], + ['git', 'config', 'user.email', DEFAULT_WPT_COMMITTER_EMAIL], ['git', 'reset', '--hard', 'HEAD'], ['git', 'clean', '-fdx'], ['git', 'checkout', 'origin/master'],
diff --git a/third_party/blink/tools/blinkpy/w3c/test_exporter_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_exporter_unittest.py index 574be29..a099068 100644 --- a/third_party/blink/tools/blinkpy/w3c/test_exporter_unittest.py +++ b/third_party/blink/tools/blinkpy/w3c/test_exporter_unittest.py
@@ -379,6 +379,7 @@ self.assertFalse(success) self.assertLog(['INFO: Cloning GitHub web-platform-tests/wpt into /tmp/wpt\n', + 'INFO: Setting git user name & email in /tmp/wpt\n', 'INFO: Searching for exportable in-flight CLs.\n', 'INFO: In-flight CLs cannot be exported due to the following error:\n', 'ERROR: Gerrit API fails.\n', @@ -393,6 +394,7 @@ self.assertFalse(success) self.assertLog(['INFO: Cloning GitHub web-platform-tests/wpt into /tmp/wpt\n', + 'INFO: Setting git user name & email in /tmp/wpt\n', 'INFO: Searching for exportable in-flight CLs.\n', 'INFO: Searching for exportable Chromium commits.\n', 'INFO: Attention: The following errors have prevented some commits from being exported:\n',
diff --git a/third_party/closure_compiler/externs/accessibility_private.js b/third_party/closure_compiler/externs/accessibility_private.js index 903fdf8..ba1d7b7 100644 --- a/third_party/closure_compiler/externs/accessibility_private.js +++ b/third_party/closure_compiler/externs/accessibility_private.js
@@ -208,3 +208,8 @@ * @type {!ChromeEvent} */ chrome.accessibilityPrivate.onSelectToSpeakStateChangeRequested; + +/** + * Called when a Switch Access user activates dictation from the context menu. + */ +chrome.accessibilityPrivate.toggleDictation = function() {};
diff --git a/third_party/feed/BUILD.gn b/third_party/feed/BUILD.gn index 0ce5943..798d3eb 100644 --- a/third_party/feed/BUILD.gn +++ b/third_party/feed/BUILD.gn
@@ -14,20 +14,41 @@ custom_package = "com.google.android.libraries.feed.piet" } -android_resources("basicstream_resources") { +android_resources("basicstream_internal_viewholders_resources") { resource_dirs = [ "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/viewholders/res" ] custom_package = "com.google.android.libraries.feed.basicstream.internal.viewholders" } +android_resources("basicstream_resources") { + resource_dirs = + [ "src/src/main/java/com/google/android/libraries/feed/basicstream/res/" ] + custom_package = "com.google.android.libraries.feed.basicstream" +} + +android_resources("shared_stream_publicapi_menumeasurer_resources") { + resource_dirs = [ "src/src/main/java/com/google/android/libraries/feed/sharedstream/publicapi/menumeasurer/res/" ] + custom_package = + "com.google.android.libraries.feed.sharedstream.publicapi.menumeasurer" +} + +android_resources("basicstream_internal_actions_resources") { + resource_dirs = [ "src/src/main/java/com/google/android/libraries/feed/basicstream/internal/actions/res/" ] + custom_package = + "com.google.android.libraries.feed.basicstream.internal.actions" +} + android_library("feed_lib_java") { chromium_code = false java_files = feed_lib_java_sources deps = [ + ":basicstream_internal_actions_resources", + ":basicstream_internal_viewholders_resources", ":basicstream_resources", ":feed_lib_proto_java", ":piet_resources", + ":shared_stream_publicapi_menumeasurer_resources", "//third_party/android_deps:android_support_annotations_java", "//third_party/android_deps:android_support_cardview_java", "//third_party/android_deps:android_support_v7_appcompat_java",
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium index c34f4df..0aa68d1 100644 --- a/third_party/feed/README.chromium +++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@ Short name: feed URL: https://chromium.googlesource.com/feed Version: 0 -Revision: 3c5a5efeacbb25d349b36c3482c5794c5229e869 +Revision: e291161061d18d222bc1b546225fe0567386cbf1 License: Apache 2.0 License File: LICENSE Security Critical: yes
diff --git a/third_party/feed/java_sources.gni b/third_party/feed/java_sources.gni index 067a06c..58fdb01 100644 --- a/third_party/feed/java_sources.gni +++ b/third_party/feed/java_sources.gni
@@ -158,6 +158,7 @@ "src/src/main/java/com/google/android/libraries/feed/host/action/StreamActionApi.java", "src/src/main/java/com/google/android/libraries/feed/host/config/Configuration.java", "src/src/main/java/com/google/android/libraries/feed/host/config/DebugBehavior.java", + "src/src/main/java/com/google/android/libraries/feed/host/imageloader/BundledAssets.java", "src/src/main/java/com/google/android/libraries/feed/host/imageloader/ImageLoaderApi.java", "src/src/main/java/com/google/android/libraries/feed/host/logging/ActionType.java", "src/src/main/java/com/google/android/libraries/feed/host/logging/BasicLoggingApi.java", @@ -228,6 +229,7 @@ "src/src/main/java/com/google/android/libraries/feed/piet/host/CustomElementProvider.java", "src/src/main/java/com/google/android/libraries/feed/piet/host/HostBindingProvider.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/DrawableScalingHelper.java", + "src/src/main/java/com/google/android/libraries/feed/piet/ui/GridRowView.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerColorDrawable.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerImageView.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerViewHelper.java", @@ -235,6 +237,8 @@ "src/src/main/java/com/google/android/libraries/feed/sharedstream/piet/PietAssetProvider.java", "src/src/main/java/com/google/android/libraries/feed/sharedstream/piet/PietCustomElementProvider.java", "src/src/main/java/com/google/android/libraries/feed/sharedstream/piet/PietHostBindingProvider.java", + "src/src/main/java/com/google/android/libraries/feed/sharedstream/publicapi/menumeasurer/MenuMeasurer.java", + "src/src/main/java/com/google/android/libraries/feed/sharedstream/publicapi/menumeasurer/Size.java", "src/src/main/java/com/google/android/libraries/feed/sharedstream/removetrackingfactory/StreamRemoveTrackingFactory.java", ]
diff --git a/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h b/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h index 689b5f1..077aef8b 100644 --- a/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h +++ b/third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h
@@ -164,6 +164,14 @@ // freed memory regions // This property is not writable. // + // "generic.total_physical_bytes" + // Estimate of total bytes of the physical memory usage by the + // allocator == + // current_allocated_bytes + + // fragmentation + + // metadata + // This property is not writable. + // // tcmalloc // -------- // "tcmalloc.max_total_thread_cache_bytes"
diff --git a/third_party/tcmalloc/chromium/src/tcmalloc.cc b/third_party/tcmalloc/chromium/src/tcmalloc.cc index 6c5bd8b..5b4d064 100644 --- a/third_party/tcmalloc/chromium/src/tcmalloc.cc +++ b/third_party/tcmalloc/chromium/src/tcmalloc.cc
@@ -723,6 +723,14 @@ return true; } + if (strcmp(name, "generic.total_physical_bytes") == 0) { + TCMallocStats stats; + ExtractStats(&stats, NULL, NULL, NULL); + *value = stats.pageheap.system_bytes + stats.metadata_bytes - + stats.pageheap.unmapped_bytes; + return true; + } + if (strcmp(name, "tcmalloc.slack_bytes") == 0) { // Kept for backwards compatibility. Now defined externally as: // pageheap_free_bytes + pageheap_unmapped_bytes.
diff --git a/tools/fuchsia/fidlgen_js/BUILD.gn b/tools/fuchsia/fidlgen_js/BUILD.gn new file mode 100644 index 0000000..7a4e8f4d --- /dev/null +++ b/tools/fuchsia/fidlgen_js/BUILD.gn
@@ -0,0 +1,60 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/fuchsia/fidl_library.gni") +import("//testing/test.gni") + +test("fidlgen_js_unittests") { + testonly = true + + sources = [ + "test/fidlgen_js_unittest.cc", + ] + + deps = [ + ":fidljstest", + ":runtime", + "//base/test:test_support", + "//gin:gin_test", + "//testing/gtest", + "//v8", + ] + + configs += [ + "//tools/v8_context_snapshot:use_v8_context_snapshot", + "//v8:external_startup_data", + ] + + data_deps = [ + "//tools/v8_context_snapshot:v8_context_snapshot", + ] + + data = [ + "runtime/fidl.mjs", + ] +} + +static_library("runtime") { + sources = [ + "runtime/zircon.cc", + "runtime/zircon.h", + ] + + deps = [ + "//gin", + "//v8", + ] +} + +fidl_library("fidljstest") { + testonly = true + sources = [ + "test/simple.fidl", + ] + + languages = [ + "cpp", + "js", + ] +}
diff --git a/tools/fuchsia/fidlgen_js/DEPS b/tools/fuchsia/fidlgen_js/DEPS new file mode 100644 index 0000000..681254d --- /dev/null +++ b/tools/fuchsia/fidlgen_js/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + "+gin", + "+v8/include", +]
diff --git a/tools/fuchsia/fidlgen_js/fidl.py b/tools/fuchsia/fidlgen_js/fidl.py new file mode 100644 index 0000000..6f8b99f --- /dev/null +++ b/tools/fuchsia/fidlgen_js/fidl.py
@@ -0,0 +1,549 @@ +# 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. + +# This was generated (and can be regenerated) by pasting +# zircon/system/host/fidl/schema.json from Fuchsia into +# https://app.quicktype.io and choosing Python 2.7 output. The only manual +# change is to modify the import path for Enum. + +from third_party.enum34 import Enum + + +def from_str(x): + assert isinstance(x, (str, unicode)) + return x + + +def from_int(x): + assert isinstance(x, int) and not isinstance(x, bool) + return x + + +def from_none(x): + assert x is None + return x + + +def from_union(fs, x): + for f in fs: + try: + return f(x) + except: + pass + assert False + + +def from_bool(x): + assert isinstance(x, bool) + return x + + +def to_class(c, x): + assert isinstance(x, c) + return x.to_dict() + + +def to_enum(c, x): + assert isinstance(x, c) + return x.value + + +def from_list(f, x): + assert isinstance(x, list) + return [f(y) for y in x] + + +def from_dict(f, x): + assert isinstance(x, dict) + return { k: f(v) for (k, v) in x.items() } + + +class Attribute: + def __init__(self, name, value): + self.name = name + self.value = value + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + name = from_str(obj.get(u"name")) + value = from_str(obj.get(u"value")) + return Attribute(name, value) + + def to_dict(self): + result = {} + result[u"name"] = from_str(self.name) + result[u"value"] = from_str(self.value) + return result + + +class TypeKind(Enum): + ARRAY = u"array" + HANDLE = u"handle" + IDENTIFIER = u"identifier" + PRIMITIVE = u"primitive" + REQUEST = u"request" + STRING = u"string" + VECTOR = u"vector" + + +class TypeClass: + def __init__(self, element_count, element_type, kind, maybe_element_count, nullable, subtype, identifier): + self.element_count = element_count + self.element_type = element_type + self.kind = kind + self.maybe_element_count = maybe_element_count + self.nullable = nullable + self.subtype = subtype + self.identifier = identifier + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + element_count = from_union([from_int, from_none], obj.get(u"element_count")) + element_type = from_union([TypeClass.from_dict, from_none], obj.get(u"element_type")) + kind = TypeKind(obj.get(u"kind")) + maybe_element_count = from_union([from_int, from_none], obj.get(u"maybe_element_count")) + nullable = from_union([from_bool, from_none], obj.get(u"nullable")) + subtype = from_union([from_str, from_none], obj.get(u"subtype")) + identifier = from_union([from_str, from_none], obj.get(u"identifier")) + return TypeClass(element_count, element_type, kind, maybe_element_count, nullable, subtype, identifier) + + def to_dict(self): + result = {} + result[u"element_count"] = from_union([from_int, from_none], self.element_count) + result[u"element_type"] = from_union([lambda x: to_class(TypeClass, x), from_none], self.element_type) + result[u"kind"] = to_enum(TypeKind, self.kind) + result[u"maybe_element_count"] = from_union([from_int, from_none], self.maybe_element_count) + result[u"nullable"] = from_union([from_bool, from_none], self.nullable) + result[u"subtype"] = from_union([from_str, from_none], self.subtype) + result[u"identifier"] = from_union([from_str, from_none], self.identifier) + return result + + +class ConstantKind(Enum): + IDENTIFIER = u"identifier" + LITERAL = u"literal" + + +class LiteralKind(Enum): + DEFAULT = u"default" + FALSE = u"false" + NUMERIC = u"numeric" + STRING = u"string" + TRUE = u"true" + + +class Literal: + def __init__(self, kind, value): + self.kind = kind + self.value = value + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + kind = LiteralKind(obj.get(u"kind")) + value = from_union([from_str, from_none], obj.get(u"value")) + return Literal(kind, value) + + def to_dict(self): + result = {} + result[u"kind"] = to_enum(LiteralKind, self.kind) + result[u"value"] = from_union([from_str, from_none], self.value) + return result + + +class Constant: + def __init__(self, identifier, kind, literal): + self.identifier = identifier + self.kind = kind + self.literal = literal + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + identifier = from_union([from_str, from_none], obj.get(u"identifier")) + kind = ConstantKind(obj.get(u"kind")) + literal = from_union([Literal.from_dict, from_none], obj.get(u"literal")) + return Constant(identifier, kind, literal) + + def to_dict(self): + result = {} + result[u"identifier"] = from_union([from_str, from_none], self.identifier) + result[u"kind"] = to_enum(ConstantKind, self.kind) + result[u"literal"] = from_union([lambda x: to_class(Literal, x), from_none], self.literal) + return result + + +class Const: + def __init__(self, maybe_attributes, name, type, value): + self.maybe_attributes = maybe_attributes + self.name = name + self.type = type + self.value = value + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + name = from_str(obj.get(u"name")) + type = TypeClass.from_dict(obj.get(u"type")) + value = Constant.from_dict(obj.get(u"value")) + return Const(maybe_attributes, name, type, value) + + def to_dict(self): + result = {} + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"name"] = from_str(self.name) + result[u"type"] = to_class(TypeClass, self.type) + result[u"value"] = to_class(Constant, self.value) + return result + + +class DeclarationsMap(Enum): + CONST = u"const" + ENUM = u"enum" + INTERFACE = u"interface" + STRUCT = u"struct" + UNION = u"union" + + +class EnumMember: + def __init__(self, name, value): + self.name = name + self.value = value + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + name = from_str(obj.get(u"name")) + value = Constant.from_dict(obj.get(u"value")) + return EnumMember(name, value) + + def to_dict(self): + result = {} + result[u"name"] = from_str(self.name) + result[u"value"] = to_class(Constant, self.value) + return result + + +class IntegerType(Enum): + INT16 = u"int16" + INT32 = u"int32" + INT64 = u"int64" + INT8 = u"int8" + UINT16 = u"uint16" + UINT32 = u"uint32" + UINT64 = u"uint64" + UINT8 = u"uint8" + + +class EnumDeclarationElement: + def __init__(self, maybe_attributes, members, name, type): + self.maybe_attributes = maybe_attributes + self.members = members + self.name = name + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + members = from_list(EnumMember.from_dict, obj.get(u"members")) + name = from_str(obj.get(u"name")) + type = IntegerType(obj.get(u"type")) + return EnumDeclarationElement(maybe_attributes, members, name, type) + + def to_dict(self): + result = {} + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"members"] = from_list(lambda x: to_class(EnumMember, x), self.members) + result[u"name"] = from_str(self.name) + result[u"type"] = to_enum(IntegerType, self.type) + return result + + +class InterfaceMethodParameter: + def __init__(self, alignment, name, offset, size, type): + self.alignment = alignment + self.name = name + self.offset = offset + self.size = size + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + alignment = from_int(obj.get(u"alignment")) + name = from_str(obj.get(u"name")) + offset = from_int(obj.get(u"offset")) + size = from_int(obj.get(u"size")) + type = TypeClass.from_dict(obj.get(u"type")) + return InterfaceMethodParameter(alignment, name, offset, size, type) + + def to_dict(self): + result = {} + result[u"alignment"] = from_int(self.alignment) + result[u"name"] = from_str(self.name) + result[u"offset"] = from_int(self.offset) + result[u"size"] = from_int(self.size) + result[u"type"] = to_class(TypeClass, self.type) + return result + + +class InterfaceMethod: + def __init__(self, has_request, has_response, maybe_attributes, maybe_request, maybe_request_alignment, maybe_request_size, maybe_response, maybe_response_alignment, maybe_response_size, name, ordinal): + self.has_request = has_request + self.has_response = has_response + self.maybe_attributes = maybe_attributes + self.maybe_request = maybe_request + self.maybe_request_alignment = maybe_request_alignment + self.maybe_request_size = maybe_request_size + self.maybe_response = maybe_response + self.maybe_response_alignment = maybe_response_alignment + self.maybe_response_size = maybe_response_size + self.name = name + self.ordinal = ordinal + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + has_request = from_bool(obj.get(u"has_request")) + has_response = from_bool(obj.get(u"has_response")) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + maybe_request = from_union([lambda x: from_list(InterfaceMethodParameter.from_dict, x), from_none], obj.get(u"maybe_request")) + maybe_request_alignment = from_union([from_int, from_none], obj.get(u"maybe_request_alignment")) + maybe_request_size = from_union([from_int, from_none], obj.get(u"maybe_request_size")) + maybe_response = from_union([lambda x: from_list(InterfaceMethodParameter.from_dict, x), from_none], obj.get(u"maybe_response")) + maybe_response_alignment = from_union([from_int, from_none], obj.get(u"maybe_response_alignment")) + maybe_response_size = from_union([from_int, from_none], obj.get(u"maybe_response_size")) + name = from_str(obj.get(u"name")) + ordinal = from_int(obj.get(u"ordinal")) + return InterfaceMethod(has_request, has_response, maybe_attributes, maybe_request, maybe_request_alignment, maybe_request_size, maybe_response, maybe_response_alignment, maybe_response_size, name, ordinal) + + def to_dict(self): + result = {} + result[u"has_request"] = from_bool(self.has_request) + result[u"has_response"] = from_bool(self.has_response) + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"maybe_request"] = from_union([lambda x: from_list(lambda x: to_class(InterfaceMethodParameter, x), x), from_none], self.maybe_request) + result[u"maybe_request_alignment"] = from_union([from_int, from_none], self.maybe_request_alignment) + result[u"maybe_request_size"] = from_union([from_int, from_none], self.maybe_request_size) + result[u"maybe_response"] = from_union([lambda x: from_list(lambda x: to_class(InterfaceMethodParameter, x), x), from_none], self.maybe_response) + result[u"maybe_response_alignment"] = from_union([from_int, from_none], self.maybe_response_alignment) + result[u"maybe_response_size"] = from_union([from_int, from_none], self.maybe_response_size) + result[u"name"] = from_str(self.name) + result[u"ordinal"] = from_int(self.ordinal) + return result + + +class Interface: + def __init__(self, maybe_attributes, methods, name): + self.maybe_attributes = maybe_attributes + self.methods = methods + self.name = name + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + methods = from_list(InterfaceMethod.from_dict, obj.get(u"methods")) + name = from_str(obj.get(u"name")) + return Interface(maybe_attributes, methods, name) + + def to_dict(self): + result = {} + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"methods"] = from_list(lambda x: to_class(InterfaceMethod, x), self.methods) + result[u"name"] = from_str(self.name) + return result + + +class Library: + def __init__(self, declarations, name): + self.declarations = declarations + self.name = name + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + declarations = from_dict(DeclarationsMap, obj.get(u"declarations")) + name = from_str(obj.get(u"name")) + return Library(declarations, name) + + def to_dict(self): + result = {} + result[u"declarations"] = from_dict(lambda x: to_enum(DeclarationsMap, x), self.declarations) + result[u"name"] = from_str(self.name) + return result + + +class StructMember: + def __init__(self, alignment, maybe_default_value, name, offset, size, type): + self.alignment = alignment + self.maybe_default_value = maybe_default_value + self.name = name + self.offset = offset + self.size = size + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + alignment = from_int(obj.get(u"alignment")) + maybe_default_value = from_union([Constant.from_dict, from_none], obj.get(u"maybe_default_value")) + name = from_str(obj.get(u"name")) + offset = from_int(obj.get(u"offset")) + size = from_int(obj.get(u"size")) + type = TypeClass.from_dict(obj.get(u"type")) + return StructMember(alignment, maybe_default_value, name, offset, size, type) + + def to_dict(self): + result = {} + result[u"alignment"] = from_int(self.alignment) + result[u"maybe_default_value"] = from_union([lambda x: to_class(Constant, x), from_none], self.maybe_default_value) + result[u"name"] = from_str(self.name) + result[u"offset"] = from_int(self.offset) + result[u"size"] = from_int(self.size) + result[u"type"] = to_class(TypeClass, self.type) + return result + + +class Struct: + def __init__(self, max_handles, maybe_attributes, members, name, size): + self.max_handles = max_handles + self.maybe_attributes = maybe_attributes + self.members = members + self.name = name + self.size = size + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + max_handles = from_union([from_int, from_none], obj.get(u"max_handles")) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + members = from_list(StructMember.from_dict, obj.get(u"members")) + name = from_str(obj.get(u"name")) + size = from_int(obj.get(u"size")) + return Struct(max_handles, maybe_attributes, members, name, size) + + def to_dict(self): + result = {} + result[u"max_handles"] = from_union([from_int, from_none], self.max_handles) + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"members"] = from_list(lambda x: to_class(StructMember, x), self.members) + result[u"name"] = from_str(self.name) + result[u"size"] = from_int(self.size) + return result + + +class UnionMember: + def __init__(self, alignment, name, offset, size, type): + self.alignment = alignment + self.name = name + self.offset = offset + self.size = size + self.type = type + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + alignment = from_int(obj.get(u"alignment")) + name = from_str(obj.get(u"name")) + offset = from_int(obj.get(u"offset")) + size = from_int(obj.get(u"size")) + type = TypeClass.from_dict(obj.get(u"type")) + return UnionMember(alignment, name, offset, size, type) + + def to_dict(self): + result = {} + result[u"alignment"] = from_int(self.alignment) + result[u"name"] = from_str(self.name) + result[u"offset"] = from_int(self.offset) + result[u"size"] = from_int(self.size) + result[u"type"] = to_class(TypeClass, self.type) + return result + + +class UnionDeclarationElement: + def __init__(self, alignment, max_handles, maybe_attributes, members, name, size): + self.alignment = alignment + self.max_handles = max_handles + self.maybe_attributes = maybe_attributes + self.members = members + self.name = name + self.size = size + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + alignment = from_int(obj.get(u"alignment")) + max_handles = from_union([from_int, from_none], obj.get(u"max_handles")) + maybe_attributes = from_union([lambda x: from_list(Attribute.from_dict, x), from_none], obj.get(u"maybe_attributes")) + members = from_list(UnionMember.from_dict, obj.get(u"members")) + name = from_str(obj.get(u"name")) + size = from_int(obj.get(u"size")) + return UnionDeclarationElement(alignment, max_handles, maybe_attributes, members, name, size) + + def to_dict(self): + result = {} + result[u"alignment"] = from_int(self.alignment) + result[u"max_handles"] = from_union([from_int, from_none], self.max_handles) + result[u"maybe_attributes"] = from_union([lambda x: from_list(lambda x: to_class(Attribute, x), x), from_none], self.maybe_attributes) + result[u"members"] = from_list(lambda x: to_class(UnionMember, x), self.members) + result[u"name"] = from_str(self.name) + result[u"size"] = from_int(self.size) + return result + + +class Fidl: + def __init__(self, const_declarations, declaration_order, declarations, enum_declarations, interface_declarations, library_dependencies, name, struct_declarations, union_declarations, version): + self.const_declarations = const_declarations + self.declaration_order = declaration_order + self.declarations = declarations + self.enum_declarations = enum_declarations + self.interface_declarations = interface_declarations + self.library_dependencies = library_dependencies + self.name = name + self.struct_declarations = struct_declarations + self.union_declarations = union_declarations + self.version = version + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + const_declarations = from_list(Const.from_dict, obj.get(u"const_declarations")) + declaration_order = from_list(from_str, obj.get(u"declaration_order")) + declarations = from_dict(DeclarationsMap, obj.get(u"declarations")) + enum_declarations = from_list(EnumDeclarationElement.from_dict, obj.get(u"enum_declarations")) + interface_declarations = from_list(Interface.from_dict, obj.get(u"interface_declarations")) + library_dependencies = from_list(Library.from_dict, obj.get(u"library_dependencies")) + name = from_str(obj.get(u"name")) + struct_declarations = from_list(Struct.from_dict, obj.get(u"struct_declarations")) + union_declarations = from_list(UnionDeclarationElement.from_dict, obj.get(u"union_declarations")) + version = from_str(obj.get(u"version")) + return Fidl(const_declarations, declaration_order, declarations, enum_declarations, interface_declarations, library_dependencies, name, struct_declarations, union_declarations, version) + + def to_dict(self): + result = {} + result[u"const_declarations"] = from_list(lambda x: to_class(Const, x), self.const_declarations) + result[u"declaration_order"] = from_list(from_str, self.declaration_order) + result[u"declarations"] = from_dict(lambda x: to_enum(DeclarationsMap, x), self.declarations) + result[u"enum_declarations"] = from_list(lambda x: to_class(EnumDeclarationElement, x), self.enum_declarations) + result[u"interface_declarations"] = from_list(lambda x: to_class(Interface, x), self.interface_declarations) + result[u"library_dependencies"] = from_list(lambda x: to_class(Library, x), self.library_dependencies) + result[u"name"] = from_str(self.name) + result[u"struct_declarations"] = from_list(lambda x: to_class(Struct, x), self.struct_declarations) + result[u"union_declarations"] = from_list(lambda x: to_class(UnionDeclarationElement, x), self.union_declarations) + result[u"version"] = from_str(self.version) + return result + + +def fidl_from_dict(s): + return Fidl.from_dict(s) + + +def fidl_to_dict(x): + return to_class(Fidl, x) +
diff --git a/tools/fuchsia/fidlgen_js/gen.py b/tools/fuchsia/fidlgen_js/gen.py new file mode 100755 index 0000000..3d3aae1 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/gen.py
@@ -0,0 +1,320 @@ +#!/usr/bin/env python + +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +from fidl import * +import json + + +class _CompoundIdentifier(object): + def __init__(self, library, name): + self.library = library + self.name = name + + +def _ParseLibraryName(lib): + return lib.split('.') + + +def _ParseCompoundIdentifier(ident): + parts = ident.split('/', 2) + raw_library = '' + raw_name = parts[0] + if len(parts) == 2: + raw_library, raw_name = parts + library = _ParseLibraryName(raw_library) + return _CompoundIdentifier(library, raw_name) + + +def _ChangeIfReserved(name): + # TODO(crbug.com/883496): Remap any JS keywords. + return name + + +def _CompileCompoundIdentifier(compound, ext=''): + result = _ChangeIfReserved(compound.name) + ext + return result + + +def _CompileIdentifier(ident): + return _ChangeIfReserved(ident) + + +def _JsTypeForPrimitiveType(t): + mapping = { + IntegerType.INT16: 'number', + IntegerType.INT32: 'number', + IntegerType.INT64: 'BigInt', + IntegerType.INT8: 'number', + IntegerType.UINT16: 'number', + IntegerType.UINT32: 'number', + IntegerType.UINT64: 'BigInt', + IntegerType.UINT8: 'number', + } + return mapping[t] + + +def _InlineSizeOfType(t): + if t.kind == TypeKind.PRIMITIVE: + return { + 'int16': 2, + 'int32': 4, + 'int64': 8, + 'int8': 1, + 'uint16': 2, + 'uint32': 4, + 'uint64': 8, + 'uint8': 1, + }[t.subtype] + else: + raise NotImplementedError() + + +def _CompileConstant(val): + if val.kind == ConstantKind.IDENTIFIER: + raise NotImplementedError() + elif val.kind == ConstantKind.LITERAL: + return _CompileLiteral(val.literal) + else: + raise Exception('unexpected kind') + + +def _CompileLiteral(val): + if val.kind == LiteralKind.STRING: + # TODO(crbug.com/883496): This needs to encode the string in an escaped + # form suitable to JS. Currently using the escaped Python representation, + # which is passably compatible, but surely has differences in edge cases. + return repr(val.value) + elif val.kind == LiteralKind.NUMERIC: + return val.value + elif val.kind == LiteralKind.TRUE: + return 'true' + elif val.kind == LiteralKind.FALSE: + return 'false' + elif val.kind == LiteralKind.DEFAULT: + return 'default' + else: + raise Exception('unexpected kind') + + +class Compiler(object): + def __init__(self, fidl, output_file): + self.fidl = fidl + self.f = output_file + + def Compile(self): + self._EmitHeader() + for c in self.fidl.const_declarations: + self._CompileConst(c) + for e in self.fidl.enum_declarations: + self._CompileEnum(e) + if self.fidl.union_declarations: + raise NotImplementedError() + if self.fidl.struct_declarations: + raise NotImplementedError() + for i in self.fidl.interface_declarations: + self._CompileInterface(i) + + def _EmitHeader(self): + self.f.write('''// 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. +// +// WARNING: This file is machine generated by fidlgen_js. + +''') + + def _CompileConst(self, c): + self.f.write('''/** + * @const {%(type)s} + */ +const %(name)s = %(value)s; +''' % c.to_dict()) + + def _CompileEnum(self, enum): + compound = _ParseCompoundIdentifier(enum.name) + name = _CompileCompoundIdentifier(compound) + js_type = _JsTypeForPrimitiveType(enum.type) + data = { 'js_type': js_type, 'type': enum.type.value, 'name': name } + self.f.write('''/** + * @enum {%(js_type)s} + */ +const %(name)s = { +''' % data) + for member in enum.members: + self.f.write(''' %s: %s,\n''' % + (member.name, _CompileConstant(member.value))) + self.f.write('};\n') + self.f.write('const _kTT_%(name)s = _kTT_%(type)s;\n\n' % data) + + + def _CompileType(self, t): + if t.kind == TypeKind.PRIMITIVE: + return t.subtype + elif t.kind == TypeKind.STRING: + return 'String' + ('_Nullable' if t.nullable else '_Nonnull') + elif t.kind == TypeKind.IDENTIFIER: + compound = _ParseCompoundIdentifier(t.identifier) + name = _CompileCompoundIdentifier(compound) + return name + elif t.kind == TypeKind.VECTOR: + element_ttname = self._CompileType(t.element_type) + ttname = ('VEC_' + ('Nullable_' if t.nullable else 'Nonnull_') + + element_ttname) + throw_if_null = '/* v may be null */' + pointer_set = ''' if (v === null || v === undefined) { + e.data.setUint32(o + 8, 0, $fidl__kLE); + e.data.setUint32(o + 12, 0, $fidl__kLE); + } else { + e.data.setUint32(o + 8, 0xffffffff, $fidl__kLE); + e.data.setUint32(o + 12, 0xffffffff, $fidl__kLE); + }''' + if not t.nullable: + throw_if_null = ('if (v === null || v === undefined) ' + 'throw "non-null vector required";') + pointer_set = ''' e.data.setUint32(o + 8, 0xffffffff, $fidl__kLE); + e.data.setUint32(o + 12, 0xffffffff, $fidl__kLE);''' + + self.f.write( +'''const _kTT_%(ttname)s = { + enc: function(e, o, v) { + %(throw_if_null)s + e.data.setUint32(o, v.length, $fidl__kLE); + e.data.setUint32(o + 4, 0, $fidl__kLE); +%(pointer_set)s + e.outOfLine.push([function(e, body) { + var start = e.alloc(body.length * %(element_size)s); + for (var i = 0; i < body.length; i++) { + _kTT_%(element_ttname)s.enc(e, start + (i * %(element_size)s), body[i]); + } + }, + v]); + }, +}; + +''' % { 'ttname': ttname, + 'element_ttname': element_ttname, + 'element_size': _InlineSizeOfType(t.element_type), + 'pointer_set': pointer_set, + 'throw_if_null': throw_if_null }) + return ttname + else: + raise NotImplementedError() + + + def _GenerateJsInterfaceForInterface(self, name, interface): + """Generates a JS @interface for the given FIDL interface.""" + self.f.write('''/** + * @interface + */ +function %(name)s() {} + +''' % { 'name': name }) + + # Define a JS interface part for the interface for typechecking. + for method in interface.methods: + method_name = _CompileIdentifier(method.name) + if method.has_request: + param_names = [_CompileIdentifier(x.name) + for x in method.maybe_request] + if len(param_names): + self.f.write('/**\n') + # TODO(crbug.com/883496): Emit @param type comments here. + self.f.write(' */\n') + self.f.write('%(name)s.prototype.%(method_name)s = ' + 'function(%(param_names)s) {};\n\n' % { + 'name': name, + 'method_name': method_name, + 'param_names': ', '.join(param_names)}) + + # Emit message ordinals for later use. + for method in interface.methods: + method_name = _CompileIdentifier(method.name) + self.f.write( + 'const _k%(name)s_%(method_name)s_Ordinal = %(ordinal)s;\n' % { + 'name': name, + 'method_name': method_name, + 'ordinal': method.ordinal}) + + self.f.write('\n') + + def _GenerateJsProxyForInterface(self, name, interface): + """Generates the JS side implementation of a proxy class implementing the + given interface.""" + proxy_name = name + 'Proxy' + self.f.write('''/** + * @constructor + * @implements %(name)s + */ +function %(proxy_name)s() { + this.channel = zx.ZX_HANDLE_INVALID; +} + +%(proxy_name)s.prototype.$bind = function(channel) { + this.channel = channel; +}; + +''' % { 'name': name, + 'proxy_name': proxy_name }) + for method in interface.methods: + method_name = _CompileIdentifier(method.name) + if method.has_request: + type_tables = [] + for param in method.maybe_request: + type_tables.append(self._CompileType(param.type)) + param_names = [_CompileIdentifier(x.name) for x in method.maybe_request] + self.f.write( +'''%(proxy_name)s.prototype.%(method_name)s = function(%(param_names)s) { + if (this.channel === zx.ZX_HANDLE_INVALID) { + throw "channel closed"; + } + var $encoder = new $fidl_Encoder(_k%(name)s_%(method_name)s_Ordinal); + $encoder.alloc(%(size)s - $fidl_kMessageHeaderSize); +''' % { 'name': name, + 'proxy_name': proxy_name, + 'method_name': method_name, + 'param_names': ', '.join(param_names), + 'size': method.maybe_request_size}) + + for param, ttname in zip(method.maybe_request, type_tables): + self.f.write( +''' _kTT_%(type_table)s.enc($encoder, %(offset)s, %(param_name)s); +''' % { 'type_table': ttname, + 'param_name': _CompileIdentifier(param.name), + 'offset': param.offset }) + + self.f.write( +''' var $result = zx.channelWrite(this.channel, + $encoder.messageData(), + $encoder.messageHandles()); + if ($result !== zx.ZX_OK) { + throw "zx.channelWrite failed: " + $result; + } +}; + +''') + + def _CompileInterface(self, interface): + compound = _ParseCompoundIdentifier(interface.name) + name = _CompileCompoundIdentifier(compound) + self._GenerateJsInterfaceForInterface(name, interface) + self._GenerateJsProxyForInterface(name, interface) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('json') + parser.add_argument('--output', required=True) + args = parser.parse_args() + + fidl = fidl_from_dict(json.load(open(args.json, 'r'))) + with open(args.output, 'w') as f: + c = Compiler(fidl, f) + c.Compile() + + +if __name__ == '__main__': + main()
diff --git a/tools/fuchsia/fidlgen_js/runtime/fidl.mjs b/tools/fuchsia/fidlgen_js/runtime/fidl.mjs new file mode 100644 index 0000000..7ccc5155 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/runtime/fidl.mjs
@@ -0,0 +1,136 @@ +// 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. + +// This is the JS runtime support library for code generated by fidlgen_js. It +// mostly consists of helpers to facilitate encoding and decoding of FIDL +// messages. + +const $fidl_kInitialBufferSize = 1024; + +const $fidl_kMessageHeaderSize = 16; +const $fidl_kMessageTxidOffset = 0; +const $fidl_kMessageOrdinalOffset = 12; + +const $fidl__kAlignment = 8; +const $fidl__kAlignmentMask = 0x7; + +const $fidl__kLE = true; + +function $fidl__align(size) { + return size + (($fidl__kAlignment - (size & $fidl__kAlignmentMask)) & + $fidl__kAlignmentMask); +} + +/** + * @constructor + * @param {number} ordinal + */ +function $fidl_Encoder(ordinal) { + var buf = new ArrayBuffer($fidl_kInitialBufferSize); + this.data = new DataView(buf); + this.extent = 0; + this.handles = []; + this.outOfLine = []; + this._encodeMessageHeader(ordinal); +} + +/** + * @param {number} ordinal + */ +$fidl_Encoder.prototype._encodeMessageHeader = function(ordinal) { + this.alloc($fidl_kMessageHeaderSize); + this.data.setUint32($fidl_kMessageOrdinalOffset, ordinal, $fidl__kLE); +}; + +/** + * @param {number} size + */ +$fidl_Encoder.prototype.alloc = function(size) { + var offset = this.extent; + this._claimMemory($fidl__align(size)); + return offset; +}; + +/** + * @param {number} claimSize + */ +$fidl_Encoder.prototype._claimMemory = function(claimSize) { + this.extent += claimSize; + if (this.extent > this.data.byteLength) { + var newSize = this.data.byteLength + claimSize; + newSize += newSize * 2; + this._grow(newSize); + } +}; + +/** + * @param {number} newSize + */ +$fidl_Encoder.prototype._grow = function(newSize) { + var newBuffer = new ArrayBuffer(newSize); + new Uint8Array(newBuffer).set(new Uint8Array(this.data.buffer)); + this.data = new DataView(newBuffer); +}; + +$fidl_Encoder.prototype.messageData = function() { + // Add all out of line data. + var len = this.outOfLine.length; + for (var i = 0; i < len; i++) { + this.outOfLine[i][0](this, this.outOfLine[i][1]); + } + + // Return final result. + return new DataView(this.data.buffer, 0, this.extent); +}; + +$fidl_Encoder.prototype.messageHandles = function() { + return this.handles; +}; + + +// Type tables and encoding helpers for generated Proxy code. +const _kTT_int8 = { + enc: function(e, o, v) { e.data.setInt8(o, v, $fidl__kLE); }, +}; + +const _kTT_int16 = { + enc: function(e, o, v) { e.data.setInt16(o, v, $fidl__kLE); }, +}; + +const _kTT_int32 = { + enc: function(e, o, v) { e.data.setUint32(o, v, $fidl__kLE); }, +}; + +const _kTT_uint8 = { + enc: function(e, o, v) { e.data.setUint8(o, v, $fidl__kLE); }, +}; + +const _kTT_uint16 = { + enc: function(e, o, v) { e.data.setUint16(o, v, $fidl__kLE); }, +}; + +const _kTT_uint32 = { + enc: function(e, o, v) { e.data.setUint32(o, v, $fidl__kLE); }, +}; + +const _kTT_String_Nonnull = { + enc: function(e, o, v) { + if (v === null || v === undefined) throw "non-null string required"; + // Both size and data are uint64, but that's awkward in JS, so for now only + // support a maximum of 32b lengths. + var asUtf8 = zx.strToUtf8Array(v); + e.data.setUint32(o, asUtf8.length, $fidl__kLE); + e.data.setUint32(o + 4, 0, $fidl__kLE); + e.data.setUint32(o + 8, 0xffffffff, $fidl__kLE); + e.data.setUint32(o + 12, 0xffffffff, $fidl__kLE); + e.outOfLine.push([$fidl_OutOfLineStringEnc, asUtf8]); + }, +}; + +function $fidl_OutOfLineStringEnc(e, strAsUtf8Array) { + var start = e.alloc(strAsUtf8Array.length); + for (var i = 0; i < strAsUtf8Array.length; i++) { + e.data.setUint8(start + i, strAsUtf8Array[i], $fidl__kLE); + } +}
diff --git a/tools/fuchsia/fidlgen_js/runtime/zircon.cc b/tools/fuchsia/fidlgen_js/runtime/zircon.cc new file mode 100644 index 0000000..7ae91489 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/runtime/zircon.cc
@@ -0,0 +1,178 @@ +// 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 "tools/fuchsia/fidlgen_js/runtime/zircon.h" + +#include <lib/zx/channel.h> +#include <zircon/errors.h> +#include <zircon/syscalls.h> +#include <zircon/types.h> + +#include "base/bind.h" +#include "gin/arguments.h" +#include "gin/array_buffer.h" +#include "gin/converter.h" +#include "gin/data_object_builder.h" +#include "gin/function_template.h" + +namespace { + +v8::Local<v8::Object> ZxChannelCreate(v8::Isolate* isolate) { + zx::channel c1, c2; + zx_status_t status = zx::channel::create(0, &c1, &c2); + return gin::DataObjectBuilder(isolate) + .Set("status", status) + .Set("first", c1.release()) + .Set("second", c2.release()) + .Build(); +} + +zx_status_t ZxChannelWrite(gin::Arguments* args) { + zx_handle_t handle; + if (!args->GetNext(&handle)) { + args->ThrowError(); + return ZX_ERR_INVALID_ARGS; + } + + gin::ArrayBufferView data; + if (!args->GetNext(&data)) { + args->ThrowError(); + return ZX_ERR_INVALID_ARGS; + } + + std::vector<zx_handle_t> handles; + if (!args->GetNext(&handles)) { + args->ThrowError(); + return ZX_ERR_INVALID_ARGS; + } + + zx_status_t status = + zx_channel_write(handle, 0, data.bytes(), data.num_bytes(), + handles.data(), handles.size()); + return status; +} + +v8::Local<v8::Value> StrToUtf8Array(gin::Arguments* args) { + std::string str; + // This converts the string to utf8 from ucs2, so then just repackage the + // string as an array and return it. + if (!args->GetNext(&str)) { + args->ThrowError(); + return v8::Local<v8::Object>(); + } + + // TODO(crbug.com/883496): Not sure how to make a Uint8Array to return here + // which would be a bit more efficient. + std::vector<int> data; + std::copy(str.begin(), str.end(), std::back_inserter(data)); + return gin::ConvertToV8(args->isolate(), data); +} + +v8::Local<v8::Object> GetOrCreateZxObject(v8::Isolate* isolate, + v8::Local<v8::Object> global) { + v8::Local<v8::Object> zx; + v8::Local<v8::Value> zx_value = global->Get(gin::StringToV8(isolate, "zx")); + if (zx_value.IsEmpty() || !zx_value->IsObject()) { + zx = v8::Object::New(isolate); + global->Set(gin::StringToSymbol(isolate, "zx"), zx); + } else { + zx = v8::Local<v8::Object>::Cast(zx); + } + return zx; +} + +} // namespace + +namespace fidljs { + +void InjectZxBindings(v8::Isolate* isolate, v8::Local<v8::Object> global) { + v8::Local<v8::Object> zx = GetOrCreateZxObject(isolate, global); + +#define SET_CONSTANT(k) \ + zx->Set(gin::StringToSymbol(isolate, #k), gin::ConvertToV8(isolate, k)) + + // zx_status_t. + SET_CONSTANT(ZX_OK); + SET_CONSTANT(ZX_ERR_INTERNAL); + SET_CONSTANT(ZX_ERR_NOT_SUPPORTED); + SET_CONSTANT(ZX_ERR_NO_RESOURCES); + SET_CONSTANT(ZX_ERR_NO_MEMORY); + SET_CONSTANT(ZX_ERR_INTERNAL_INTR_RETRY); + SET_CONSTANT(ZX_ERR_INVALID_ARGS); + SET_CONSTANT(ZX_ERR_BAD_HANDLE); + SET_CONSTANT(ZX_ERR_WRONG_TYPE); + SET_CONSTANT(ZX_ERR_BAD_SYSCALL); + SET_CONSTANT(ZX_ERR_OUT_OF_RANGE); + SET_CONSTANT(ZX_ERR_BUFFER_TOO_SMALL); + SET_CONSTANT(ZX_ERR_BAD_STATE); + SET_CONSTANT(ZX_ERR_TIMED_OUT); + SET_CONSTANT(ZX_ERR_SHOULD_WAIT); + SET_CONSTANT(ZX_ERR_CANCELED); + SET_CONSTANT(ZX_ERR_PEER_CLOSED); + SET_CONSTANT(ZX_ERR_NOT_FOUND); + SET_CONSTANT(ZX_ERR_ALREADY_EXISTS); + SET_CONSTANT(ZX_ERR_ALREADY_BOUND); + SET_CONSTANT(ZX_ERR_UNAVAILABLE); + SET_CONSTANT(ZX_ERR_ACCESS_DENIED); + SET_CONSTANT(ZX_ERR_IO); + SET_CONSTANT(ZX_ERR_IO_REFUSED); + SET_CONSTANT(ZX_ERR_IO_DATA_INTEGRITY); + SET_CONSTANT(ZX_ERR_IO_DATA_LOSS); + SET_CONSTANT(ZX_ERR_IO_NOT_PRESENT); + SET_CONSTANT(ZX_ERR_IO_OVERRUN); + SET_CONSTANT(ZX_ERR_IO_MISSED_DEADLINE); + SET_CONSTANT(ZX_ERR_IO_INVALID); + SET_CONSTANT(ZX_ERR_BAD_PATH); + SET_CONSTANT(ZX_ERR_NOT_DIR); + SET_CONSTANT(ZX_ERR_NOT_FILE); + SET_CONSTANT(ZX_ERR_FILE_BIG); + SET_CONSTANT(ZX_ERR_NO_SPACE); + SET_CONSTANT(ZX_ERR_NOT_EMPTY); + SET_CONSTANT(ZX_ERR_STOP); + SET_CONSTANT(ZX_ERR_NEXT); + SET_CONSTANT(ZX_ERR_ASYNC); + SET_CONSTANT(ZX_ERR_PROTOCOL_NOT_SUPPORTED); + SET_CONSTANT(ZX_ERR_ADDRESS_UNREACHABLE); + SET_CONSTANT(ZX_ERR_ADDRESS_IN_USE); + SET_CONSTANT(ZX_ERR_NOT_CONNECTED); + SET_CONSTANT(ZX_ERR_CONNECTION_REFUSED); + SET_CONSTANT(ZX_ERR_CONNECTION_RESET); + SET_CONSTANT(ZX_ERR_CONNECTION_ABORTED); + + // Handle APIs. + zx->Set(gin::StringToSymbol(isolate, "handleClose"), + gin::CreateFunctionTemplate(isolate, + base::BindRepeating(&zx_handle_close)) + ->GetFunction()); + SET_CONSTANT(ZX_HANDLE_INVALID); + + // Channel APIs. + zx->Set(gin::StringToSymbol(isolate, "channelCreate"), + gin::CreateFunctionTemplate(isolate, + base::BindRepeating(&ZxChannelCreate)) + ->GetFunction()); + zx->Set( + gin::StringToSymbol(isolate, "channelWrite"), + gin::CreateFunctionTemplate(isolate, base::BindRepeating(&ZxChannelWrite)) + ->GetFunction()); + SET_CONSTANT(ZX_CHANNEL_READABLE); + SET_CONSTANT(ZX_CHANNEL_WRITABLE); + SET_CONSTANT(ZX_CHANNEL_PEER_CLOSED); + SET_CONSTANT(ZX_CHANNEL_READ_MAY_DISCARD); + SET_CONSTANT(ZX_CHANNEL_MAX_MSG_BYTES); + SET_CONSTANT(ZX_CHANNEL_MAX_MSG_HANDLES); + + // Utility to make string handling easier to convert from a UCS2 JS string to + // an array of UTF-8 (which is how strings are represented in FIDL). + // TODO(crbug.com/883496): This is not really zx, should move to a generic + // runtime helper file if there are more similar C++ helpers required. + zx->Set( + gin::StringToSymbol(isolate, "strToUtf8Array"), + gin::CreateFunctionTemplate(isolate, base::BindRepeating(&StrToUtf8Array)) + ->GetFunction()); + +#undef SET_CONSTANT +} + +} // namespace fidljs
diff --git a/tools/fuchsia/fidlgen_js/runtime/zircon.h b/tools/fuchsia/fidlgen_js/runtime/zircon.h new file mode 100644 index 0000000..7bf6ea7 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/runtime/zircon.h
@@ -0,0 +1,17 @@ +// 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 TOOLS_FUCHSIA_FIDLGEN_JS_RUNTIME_ZIRCON_H_ +#define TOOLS_FUCHSIA_FIDLGEN_JS_RUNTIME_ZIRCON_H_ + +#include "v8/include/v8.h" + +namespace fidljs { + +// Adds Zircon APIs bindings to |global|, for use by JavaScript callers. +void InjectZxBindings(v8::Isolate* isolate, v8::Local<v8::Object> global); + +} // namespace fidljs + +#endif // TOOLS_FUCHSIA_FIDLGEN_JS_RUNTIME_ZIRCON_H_
diff --git a/tools/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc b/tools/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc new file mode 100644 index 0000000..260fd1c --- /dev/null +++ b/tools/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
@@ -0,0 +1,332 @@ +// 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 "base/bind.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "gin/converter.h" +#include "gin/modules/console.h" +#include "gin/object_template_builder.h" +#include "gin/public/isolate_holder.h" +#include "gin/shell_runner.h" +#include "gin/test/v8_test.h" +#include "gin/try_catch.h" +#include "tools/fuchsia/fidlgen_js/fidl/fidljstest/cpp/fidl.h" +#include "tools/fuchsia/fidlgen_js/runtime/zircon.h" +#include "v8/include/v8.h" + +static const char kRuntimeFile[] = + "/pkg/tools/fuchsia/fidlgen_js/runtime/fidl.mjs"; +static const char kTestBindingFile[] = + "/pkg/tools/fuchsia/fidlgen_js/fidl/fidljstest/js/fidl.js"; + +class FidlGenJsTestShellRunnerDelegate : public gin::ShellRunnerDelegate { + public: + FidlGenJsTestShellRunnerDelegate() {} + + v8::Local<v8::ObjectTemplate> GetGlobalTemplate( + gin::ShellRunner* runner, + v8::Isolate* isolate) override { + v8::Local<v8::ObjectTemplate> templ = + gin::ObjectTemplateBuilder(isolate).Build(); + gin::Console::Register(isolate, templ); + return templ; + } + + void UnhandledException(gin::ShellRunner* runner, + gin::TryCatch& try_catch) override { + LOG(ERROR) << try_catch.GetStackTrace(); + ADD_FAILURE(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FidlGenJsTestShellRunnerDelegate); +}; + +using FidlGenJsTest = gin::V8Test; + +TEST_F(FidlGenJsTest, BasicJSSetup) { + v8::Isolate* isolate = instance_->isolate(); + + std::string source = "log('this is a log'); this.stuff = 'HAI';"; + FidlGenJsTestShellRunnerDelegate delegate; + gin::ShellRunner runner(&delegate, isolate); + gin::Runner::Scope scope(&runner); + runner.Run(source, "test.js"); + + std::string result; + EXPECT_TRUE(gin::Converter<std::string>::FromV8( + isolate, runner.global()->Get(gin::StringToV8(isolate, "stuff")), + &result)); + EXPECT_EQ("HAI", result); +} + +TEST_F(FidlGenJsTest, CreateChannelPair) { + v8::Isolate* isolate = instance_->isolate(); + v8::HandleScope handle_scope(isolate); + + FidlGenJsTestShellRunnerDelegate delegate; + gin::ShellRunner runner(&delegate, isolate); + gin::Runner::Scope scope(&runner); + + fidljs::InjectZxBindings(isolate, runner.global()); + + std::string source = R"( + var result = zx.channelCreate(); + this.result_status = result.status; + this.result_h1 = result.first; + this.result_h2 = result.second; + if (result.status == zx.ZX_OK) { + zx.handleClose(result.first); + zx.handleClose(result.second); + } + )"; + + runner.Run(source, "test.js"); + + zx_status_t status = ZX_ERR_INTERNAL; + EXPECT_TRUE(gin::Converter<zx_status_t>::FromV8( + isolate, runner.global()->Get(gin::StringToV8(isolate, "result_status")), + &status)); + EXPECT_EQ(status, ZX_OK); + + zx_handle_t handle = ZX_HANDLE_INVALID; + + EXPECT_TRUE(gin::Converter<zx_handle_t>::FromV8( + isolate, runner.global()->Get(gin::StringToV8(isolate, "result_h1")), + &handle)); + EXPECT_NE(handle, ZX_HANDLE_INVALID); + + EXPECT_TRUE(gin::Converter<zx_handle_t>::FromV8( + isolate, runner.global()->Get(gin::StringToV8(isolate, "result_h2")), + &handle)); + EXPECT_NE(handle, ZX_HANDLE_INVALID); +} + +class TestolaImpl : public fidljstest::Testola { + public: + TestolaImpl() = default; + ~TestolaImpl() override {} + + void DoSomething() override { was_do_something_called_ = true; } + + void PrintInt(int32_t number) override { received_int_ = number; } + + void PrintMsg(fidl::StringPtr message) override { + std::string as_str = message.get(); + received_msg_ = as_str; + } + + void VariousArgs(fidljstest::Blorp blorp, + fidl::StringPtr msg, + fidl::VectorPtr<uint32_t> stuff) override { + std::string msg_as_str = msg.get(); + std::vector<uint32_t> stuff_as_vec = stuff.get(); + various_blorp_ = blorp; + various_msg_ = msg_as_str; + various_stuff_ = stuff_as_vec; + } + + bool was_do_something_called() const { return was_do_something_called_; } + int32_t received_int() const { return received_int_; } + const std::string& received_msg() const { return received_msg_; } + + fidljstest::Blorp various_blorp() const { return various_blorp_; } + const std::string& various_msg() const { return various_msg_; } + const std::vector<uint32_t>& various_stuff() const { return various_stuff_; } + + private: + bool was_do_something_called_ = false; + int32_t received_int_ = -1; + std::string received_msg_; + fidljstest::Blorp various_blorp_; + std::string various_msg_; + std::vector<uint32_t> various_stuff_; + + DISALLOW_COPY_AND_ASSIGN(TestolaImpl); +}; + +void LoadAndSource(gin::ShellRunner* runner, const base::FilePath& filename) { + std::string contents; + ASSERT_TRUE(base::ReadFileToString(filename, &contents)); + + runner->Run(contents, filename.MaybeAsASCII()); +} + +class BindingsSetupHelper { + public: + explicit BindingsSetupHelper(v8::Isolate* isolate) + : handle_scope_(isolate), delegate_(), runner_(&delegate_, isolate) { + gin::Runner::Scope scope(&runner_); + + fidljs::InjectZxBindings(isolate, runner_.global()); + + // TODO(crbug.com/883496): Figure out how to set up v8 import hooking and + // make fidl_Xyz into $fidl.Xyz. Manually inject the runtime support js + // files for now. + LoadAndSource(&runner_, base::FilePath(kRuntimeFile)); + LoadAndSource(&runner_, base::FilePath(kTestBindingFile)); + + zx_status_t status = zx::channel::create(0, &server_, &client_); + EXPECT_EQ(status, ZX_OK); + + runner_.global()->Set(gin::StringToSymbol(isolate, "testHandle"), + gin::ConvertToV8(isolate, client_.get())); + } + + zx::channel& server() { return server_; } + zx::channel& client() { return client_; } + gin::ShellRunner& runner() { return runner_; } + + private: + v8::HandleScope handle_scope_; + FidlGenJsTestShellRunnerDelegate delegate_; + gin::ShellRunner runner_; + zx::channel server_; + zx::channel client_; + + DISALLOW_COPY_AND_ASSIGN(BindingsSetupHelper); +}; + +TEST_F(FidlGenJsTest, RawReceiveFidlMessage) { + v8::Isolate* isolate = instance_->isolate(); + BindingsSetupHelper helper(isolate); + + // Send the data from the JS side into the channel. + std::string source = R"( + var proxy = new TestolaProxy(); + proxy.$bind(testHandle); + proxy.DoSomething(); + )"; + helper.runner().Run(source, "test.js"); + + // Read it out, decode, and confirm it was dispatched. + TestolaImpl testola_impl; + fidljstest::Testola_Stub stub(&testola_impl); + uint8_t data[1024]; + zx_handle_t handles[1]; + uint32_t actual_bytes, actual_handles; + ASSERT_EQ(helper.server().read(0, data, base::size(data), &actual_bytes, + handles, base::size(handles), &actual_handles), + ZX_OK); + EXPECT_EQ(actual_bytes, 16u); + EXPECT_EQ(actual_handles, 0u); + + fidl::Message message( + fidl::BytePart(data, actual_bytes, actual_bytes), + fidl::HandlePart(handles, actual_handles, actual_handles)); + stub.Dispatch_(std::move(message), fidl::internal::PendingResponse()); + + EXPECT_TRUE(testola_impl.was_do_something_called()); +} + +TEST_F(FidlGenJsTest, RawReceiveFidlMessageWithSimpleArg) { + v8::Isolate* isolate = instance_->isolate(); + BindingsSetupHelper helper(isolate); + + // Send the data from the JS side into the channel. + std::string source = R"( + var proxy = new TestolaProxy(); + proxy.$bind(testHandle); + proxy.PrintInt(12345); + )"; + helper.runner().Run(source, "test.js"); + + // Read it out, decode, and confirm it was dispatched. + TestolaImpl testola_impl; + fidljstest::Testola_Stub stub(&testola_impl); + uint8_t data[1024]; + zx_handle_t handles[1]; + uint32_t actual_bytes, actual_handles; + ASSERT_EQ(helper.server().read(0, data, base::size(data), &actual_bytes, + handles, base::size(handles), &actual_handles), + ZX_OK); + // 24 rather than 20 because everything's 8 aligned. + EXPECT_EQ(actual_bytes, 24u); + EXPECT_EQ(actual_handles, 0u); + + fidl::Message message( + fidl::BytePart(data, actual_bytes, actual_bytes), + fidl::HandlePart(handles, actual_handles, actual_handles)); + stub.Dispatch_(std::move(message), fidl::internal::PendingResponse()); + + EXPECT_EQ(testola_impl.received_int(), 12345); +} + +TEST_F(FidlGenJsTest, RawReceiveFidlMessageWithStringArg) { + v8::Isolate* isolate = instance_->isolate(); + BindingsSetupHelper helper(isolate); + + // Send the data from the JS side into the channel. + std::string source = R"( + var proxy = new TestolaProxy(); + proxy.$bind(testHandle); + proxy.PrintMsg('Ça c\'est a ä½ å¥½ from deep in JS'); + )"; + helper.runner().Run(source, "test.js"); + + // Read it out, decode, and confirm it was dispatched. + TestolaImpl testola_impl; + fidljstest::Testola_Stub stub(&testola_impl); + uint8_t data[1024]; + zx_handle_t handles[1]; + uint32_t actual_bytes, actual_handles; + ASSERT_EQ(helper.server().read(0, data, base::size(data), &actual_bytes, + handles, base::size(handles), &actual_handles), + ZX_OK); + EXPECT_EQ(actual_handles, 0u); + + fidl::Message message( + fidl::BytePart(data, actual_bytes, actual_bytes), + fidl::HandlePart(handles, actual_handles, actual_handles)); + stub.Dispatch_(std::move(message), fidl::internal::PendingResponse()); + + EXPECT_EQ(testola_impl.received_msg(), "Ça c'est a ä½ å¥½ from deep in JS"); +} + +TEST_F(FidlGenJsTest, RawReceiveFidlMessageWithMultipleArgs) { + v8::Isolate* isolate = instance_->isolate(); + BindingsSetupHelper helper(isolate); + + // Send the data from the JS side into the channel. + std::string source = R"( + var proxy = new TestolaProxy(); + proxy.$bind(testHandle); + proxy.VariousArgs(Blorp.GAMMA, 'zippy zap', [ 999, 987, 123456 ]); + )"; + helper.runner().Run(source, "test.js"); + + // Read it out, decode, and confirm it was dispatched. + TestolaImpl testola_impl; + fidljstest::Testola_Stub stub(&testola_impl); + uint8_t data[1024]; + zx_handle_t handles[1]; + uint32_t actual_bytes, actual_handles; + ASSERT_EQ(helper.server().read(0, data, base::size(data), &actual_bytes, + handles, base::size(handles), &actual_handles), + ZX_OK); + EXPECT_EQ(actual_handles, 0u); + + fidl::Message message( + fidl::BytePart(data, actual_bytes, actual_bytes), + fidl::HandlePart(handles, actual_handles, actual_handles)); + stub.Dispatch_(std::move(message), fidl::internal::PendingResponse()); + + EXPECT_EQ(testola_impl.various_blorp(), fidljstest::Blorp::GAMMA); + EXPECT_EQ(testola_impl.various_msg(), "zippy zap"); + ASSERT_EQ(testola_impl.various_stuff().size(), 3u); + EXPECT_EQ(testola_impl.various_stuff()[0], 999u); + EXPECT_EQ(testola_impl.various_stuff()[1], 987u); + EXPECT_EQ(testola_impl.various_stuff()[2], 123456u); +} + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +}
diff --git a/tools/fuchsia/fidlgen_js/test/simple.fidl b/tools/fuchsia/fidlgen_js/test/simple.fidl new file mode 100644 index 0000000..95d5b84 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/test/simple.fidl
@@ -0,0 +1,21 @@ +// 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. + +library fidljstest; + +enum Blorp : int8 { + ALPHA = 1; + BETA = 2; + GAMMA = 0x48; +}; + +interface Testola { + 1: DoSomething(); + + 2: PrintInt(int32 num); + + 3: PrintMsg(string msg); + + 4: VariousArgs(Blorp blorp, string:32 msg, vector<uint32> stuff); +};
diff --git a/tools/fuchsia/fidlgen_js/third_party/__init__.py b/tools/fuchsia/fidlgen_js/third_party/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/third_party/__init__.py
diff --git a/tools/fuchsia/fidlgen_js/third_party/enum34/LICENSE b/tools/fuchsia/fidlgen_js/third_party/enum34/LICENSE new file mode 100644 index 0000000..9003b885 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/third_party/enum34/LICENSE
@@ -0,0 +1,32 @@ +Copyright (c) 2013, Ethan Furman. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + Neither the name Ethan Furman nor the names of any + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.
diff --git a/tools/fuchsia/fidlgen_js/third_party/enum34/README.chromium b/tools/fuchsia/fidlgen_js/third_party/enum34/README.chromium new file mode 100644 index 0000000..4d0ef07 --- /dev/null +++ b/tools/fuchsia/fidlgen_js/third_party/enum34/README.chromium
@@ -0,0 +1,15 @@ +Name: enum34 +Short Name: enum34 +URL: https://bitbucket.org/stoneleaf/enum34 +License: BSD +License File: LICENSE +Revision: f24487b +Security Critical: no + + +Description: + +'Enum' backported from Python 3.4 to earlier Python versions. Only LICENSE and +__init__.py are taken, other packaging files, documentation, etc. removed. + +Only used at build time.
diff --git a/tools/fuchsia/fidlgen_js/third_party/enum34/__init__.py b/tools/fuchsia/fidlgen_js/third_party/enum34/__init__.py new file mode 100644 index 0000000..d6ffb3a --- /dev/null +++ b/tools/fuchsia/fidlgen_js/third_party/enum34/__init__.py
@@ -0,0 +1,837 @@ +"""Python Enumerations""" + +import sys as _sys + +__all__ = ['Enum', 'IntEnum', 'unique'] + +version = 1, 1, 6 + +pyver = float('%s.%s' % _sys.version_info[:2]) + +try: + any +except NameError: + def any(iterable): + for element in iterable: + if element: + return True + return False + +try: + from collections import OrderedDict +except ImportError: + OrderedDict = None + +try: + basestring +except NameError: + # In Python 2 basestring is the ancestor of both str and unicode + # in Python 3 it's just str, but was missing in 3.1 + basestring = str + +try: + unicode +except NameError: + # In Python 3 unicode no longer exists (it's just str) + unicode = str + +class _RouteClassAttributeToGetattr(object): + """Route attribute access on a class to __getattr__. + + This is a descriptor, used to define attributes that act differently when + accessed through an instance and through a class. Instance access remains + normal, but access to an attribute through a class will be routed to the + class's __getattr__ method; this is done by raising AttributeError. + + """ + def __init__(self, fget=None): + self.fget = fget + + def __get__(self, instance, ownerclass=None): + if instance is None: + raise AttributeError() + return self.fget(instance) + + def __set__(self, instance, value): + raise AttributeError("can't set attribute") + + def __delete__(self, instance): + raise AttributeError("can't delete attribute") + + +def _is_descriptor(obj): + """Returns True if obj is a descriptor, False otherwise.""" + return ( + hasattr(obj, '__get__') or + hasattr(obj, '__set__') or + hasattr(obj, '__delete__')) + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (name[:2] == name[-2:] == '__' and + name[2:3] != '_' and + name[-3:-2] != '_' and + len(name) > 4) + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_' and + len(name) > 2) + + +def _make_class_unpicklable(cls): + """Make the given class un-picklable.""" + def _break_on_call_reduce(self, protocol=None): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce_ex__ = _break_on_call_reduce + cls.__module__ = '<unknown>' + + +class _EnumDict(dict): + """Track enum member order and ensure member names are not reused. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + + """ + def __init__(self): + super(_EnumDict, self).__init__() + self._member_names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or not a descriptor. + + If a descriptor is added with the same name as an enum member, the name + is removed from _member_names (this may leave a hole in the numerical + sequence of values). + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + + Note: in 3.x __order__ is simply discarded as a not necessary piece + leftover from 2.x + + """ + if pyver >= 3.0 and key in ('_order_', '__order__'): + return + elif key == '__order__': + key = '_order_' + if _is_sunder(key): + if key != '_order_': + raise ValueError('_names_ are reserved for future Enum use') + elif _is_dunder(key): + pass + elif key in self._member_names: + # descriptor overwriting an enum? + raise TypeError('Attempted to reuse key: %r' % key) + elif not _is_descriptor(value): + if key in self: + # enum overwriting a descriptor? + raise TypeError('Key already defined as: %r' % self[key]) + self._member_names.append(key) + super(_EnumDict, self).__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicity checks for it, but of course until +# EnumMeta finishes running the first time the Enum class doesn't exist. This +# is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + + +class EnumMeta(type): + """Metaclass for Enum""" + @classmethod + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + if type(classdict) is dict: + original_dict = classdict + classdict = _EnumDict() + for k, v in original_dict.items(): + classdict[k] = v + + member_type, first_enum = metacls._get_mixins_(bases) + __new__, save_new, use_args = metacls._find_new_(classdict, member_type, + first_enum) + # save enum items into separate mapping so they don't get baked into + # the new class + members = dict((k, classdict[k]) for k in classdict._member_names) + for name in classdict._member_names: + del classdict[name] + + # py2 support for definition order + _order_ = classdict.get('_order_') + if _order_ is None: + if pyver < 3.0: + try: + _order_ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])] + except TypeError: + _order_ = [name for name in sorted(members.keys())] + else: + _order_ = classdict._member_names + else: + del classdict['_order_'] + if pyver < 3.0: + _order_ = _order_.replace(',', ' ').split() + aliases = [name for name in members if name not in _order_] + _order_ += aliases + + # check for illegal enum names (any others?) + invalid_names = set(members) & set(['mro']) + if invalid_names: + raise ValueError('Invalid enum member name(s): %s' % ( + ', '.join(invalid_names), )) + + # save attributes from super classes so we know if we can take + # the shortcut of storing members in the class dict + base_attributes = set([a for b in bases for a in b.__dict__]) + # create our new Enum type + enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict) + enum_class._member_names_ = [] # names in random order + if OrderedDict is not None: + enum_class._member_map_ = OrderedDict() + else: + enum_class._member_map_ = {} # name->value map + enum_class._member_type_ = member_type + + # Reverse value->name map for hashable values. + enum_class._value2member_map_ = {} + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + if __new__ is None: + __new__ = enum_class.__new__ + for member_name in _order_: + value = members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args or not args: + enum_member = __new__(enum_class) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = member_type(*args) + value = enum_member._value_ + enum_member._name_ = member_name + enum_member.__objclass__ = enum_class + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map_.items(): + if canonical_member.value == enum_member._value_: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names_.append(member_name) + # performance boost for any member that would not shadow + # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr) + if member_name not in base_attributes: + setattr(enum_class, member_name, enum_member) + # now add to _member_map_ + enum_class._member_map_[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_[value] = enum_member + except TypeError: + pass + + + # If a custom type is mixed into the Enum, and it does not know how + # to pickle itself, pickle.dumps will succeed but pickle.loads will + # fail. Rather than have the error show up later and possibly far + # from the source, sabotage the pickle protocol for this class so + # that pickle.dumps also fails. + # + # However, if the new class implements its own __reduce_ex__, do not + # sabotage -- it's on them to make sure it works correctly. We use + # __reduce_ex__ instead of any of the others as it is preferred by + # pickle over __reduce__, and it handles all pickle protocols. + unpicklable = False + if '__reduce_ex__' not in classdict: + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(m in member_type.__dict__ for m in methods): + _make_class_unpicklable(enum_class) + unpicklable = True + + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if name not in classdict and class_method is not enum_method: + if name == '__reduce_ex__' and unpicklable: + continue + setattr(enum_class, name, enum_method) + + # method resolution and int's are not playing nice + # Python's less than 2.6 use __cmp__ + + if pyver < 2.6: + + if issubclass(enum_class, int): + setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) + + elif pyver < 3.0: + + if issubclass(enum_class, int): + for method in ( + '__le__', + '__lt__', + '__gt__', + '__ge__', + '__eq__', + '__ne__', + '__hash__', + ): + setattr(enum_class, method, getattr(int, method)) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + setattr(enum_class, '__member_new__', enum_class.__dict__['__new__']) + setattr(enum_class, '__new__', Enum.__dict__['__new__']) + return enum_class + + def __bool__(cls): + """ + classes/types should always be True. + """ + return True + + def __call__(cls, value, names=None, module=None, type=None, start=1): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, type=type, start=start) + + def __contains__(cls, member): + return isinstance(member, cls) and member.name in cls._member_map_ + + def __delattr__(cls, attr): + # nicer error message when someone tries to delete an attribute + # (see issue19025). + if attr in cls._member_map_: + raise AttributeError( + "%s: cannot delete Enum member." % cls.__name__) + super(EnumMeta, cls).__delattr__(attr) + + def __dir__(self): + return (['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_) + + @property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a copy of the internal mapping. + + """ + return cls._member_map_.copy() + + def __getattr__(cls, name): + """Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map_[name] + except KeyError: + raise AttributeError(name) + + def __getitem__(cls, name): + return cls._member_map_[name] + + def __iter__(cls): + return (cls._member_map_[name] for name in cls._member_names_) + + def __reversed__(cls): + return (cls._member_map_[name] for name in reversed(cls._member_names_)) + + def __len__(cls): + return len(cls._member_names_) + + __nonzero__ = __bool__ + + def __repr__(cls): + return "<enum %r>" % cls.__name__ + + def __setattr__(cls, name, value): + """Block attempts to reassign Enum members. + + A simple assignment to the class namespace only changes one of the + several possible ways to get an Enum member from the Enum class, + resulting in an inconsistent Enumeration. + + """ + member_map = cls.__dict__.get('_member_map_', {}) + if name in member_map: + raise AttributeError('Cannot reassign members.') + super(EnumMeta, cls).__setattr__(name, value) + + def _create_(cls, class_name, names=None, module=None, type=None, start=1): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + """ + if pyver < 3.0: + # if class_name is unicode, attempt a conversion to ASCII + if isinstance(class_name, unicode): + try: + class_name = class_name.encode('ascii') + except UnicodeEncodeError: + raise TypeError('%r is not representable in ASCII' % class_name) + metacls = cls.__class__ + if type is None: + bases = (cls, ) + else: + bases = (type, cls) + classdict = metacls.__prepare__(class_name, bases) + _order_ = [] + + # special processing needed for names? + if isinstance(names, basestring): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): + names = [(e, i+start) for (i, e) in enumerate(names)] + + # Here, names is either an iterable of (name, value) or a mapping. + item = None # in case names is empty + for item in names: + if isinstance(item, basestring): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + _order_.append(member_name) + # only set _order_ in classdict if name/value was not from a mapping + if not isinstance(item, basestring): + classdict['_order_'] = ' '.join(_order_) + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = _sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError): + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + + return enum_class + + @staticmethod + def _get_mixins_(bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + + """ + if not bases or Enum is None: + return object, Enum + + + # double check that we are not subclassing a class with existing + # enumeration members; while we're at it, see if any other data + # type has been mixed in so we can use the correct __new__ + member_type = first_enum = None + for base in bases: + if (base is not Enum and + issubclass(base, Enum) and + base._member_names_): + raise TypeError("Cannot extend enumerations") + # base is now the last base in bases + if not issubclass(base, Enum): + raise TypeError("new enumerations must be created as " + "`ClassName([mixin_type,] enum_type)`") + + # get correct mix-in type (either mix-in type of Enum subclass, or + # first base if last base is Enum) + if not issubclass(bases[0], Enum): + member_type = bases[0] # first data type + first_enum = bases[-1] # enum type + else: + for base in bases[0].__mro__: + # most common: (IntEnum, int, Enum, object) + # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>, + # <class 'int'>, <Enum 'Enum'>, + # <class 'object'>) + if issubclass(base, Enum): + if first_enum is None: + first_enum = base + else: + if member_type is None: + member_type = base + + return member_type, first_enum + + if pyver < 3.0: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + if __new__: + return None, True, True # __new__, save_new, use_args + + N__new__ = getattr(None, '__new__') + O__new__ = getattr(object, '__new__') + if Enum is None: + E__new__ = N__new__ + else: + E__new__ = Enum.__dict__['__new__'] + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + try: + target = possible.__dict__[method] + except (AttributeError, KeyError): + target = getattr(possible, method, None) + if target not in [ + None, + N__new__, + O__new__, + E__new__, + ]: + if method == '__member_new__': + classdict['__new__'] = target + return None, False, True + if isinstance(target, staticmethod): + target = target.__get__(member_type) + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, False, use_args + else: + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __member_new__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __member_new__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __member_new__ before falling back to + # __new__ + for method in ('__member_new__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in ( + None, + None.__new__, + object.__new__, + Enum.__new__, + ): + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + + return __new__, save_new, use_args + + +######################################################## +# In order to support Python 2 and 3 with a single +# codebase we have to create the Enum methods separately +# and then use the `type(name, bases, dict)` method to +# create the class. +######################################################## +temp_enum_dict = {} +temp_enum_dict['__doc__'] = "Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n" + +def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.red) + value = value.value + #return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + try: + if value in cls._value2member_map_: + return cls._value2member_map_[value] + except TypeError: + # not there, now do long search -- O(n) behavior + for member in cls._member_map_.values(): + if member.value == value: + return member + raise ValueError("%s is not a valid %s" % (value, cls.__name__)) +temp_enum_dict['__new__'] = __new__ +del __new__ + +def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name_, self._value_) +temp_enum_dict['__repr__'] = __repr__ +del __repr__ + +def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name_) +temp_enum_dict['__str__'] = __str__ +del __str__ + +if pyver >= 3.0: + def __dir__(self): + added_behavior = [ + m + for cls in self.__class__.mro() + for m in cls.__dict__ + if m[0] != '_' and m not in self._member_map_ + ] + return (['__class__', '__doc__', '__module__', ] + added_behavior) + temp_enum_dict['__dir__'] = __dir__ + del __dir__ + +def __format__(self, format_spec): + # mixed-in Enums should use the mixed-in type's __format__, otherwise + # we can get strange results with the Enum name showing up instead of + # the value + + # pure Enum branch + if self._member_type_ is object: + cls = str + val = str(self) + # mix-in branch + else: + cls = self._member_type_ + val = self.value + return cls.__format__(val, format_spec) +temp_enum_dict['__format__'] = __format__ +del __format__ + + +#################################### +# Python's less than 2.6 use __cmp__ + +if pyver < 2.6: + + def __cmp__(self, other): + if type(other) is self.__class__: + if self is other: + return 0 + return -1 + return NotImplemented + raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__cmp__'] = __cmp__ + del __cmp__ + +else: + + def __le__(self, other): + raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__le__'] = __le__ + del __le__ + + def __lt__(self, other): + raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__lt__'] = __lt__ + del __lt__ + + def __ge__(self, other): + raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__ge__'] = __ge__ + del __ge__ + + def __gt__(self, other): + raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) + temp_enum_dict['__gt__'] = __gt__ + del __gt__ + + +def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented +temp_enum_dict['__eq__'] = __eq__ +del __eq__ + +def __ne__(self, other): + if type(other) is self.__class__: + return self is not other + return NotImplemented +temp_enum_dict['__ne__'] = __ne__ +del __ne__ + +def __hash__(self): + return hash(self._name_) +temp_enum_dict['__hash__'] = __hash__ +del __hash__ + +def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) +temp_enum_dict['__reduce_ex__'] = __reduce_ex__ +del __reduce_ex__ + +# _RouteClassAttributeToGetattr is used to provide access to the `name` +# and `value` properties of enum members while keeping some measure of +# protection from modification, while still allowing for an enumeration +# to have members named `name` and `value`. This works because enumeration +# members are not set directly on the enum class -- __getattr__ is +# used to look them up. + +@_RouteClassAttributeToGetattr +def name(self): + return self._name_ +temp_enum_dict['name'] = name +del name + +@_RouteClassAttributeToGetattr +def value(self): + return self._value_ +temp_enum_dict['value'] = value +del value + +@classmethod +def _convert(cls, name, module, filter, source=None): + """ + Create a new Enum subclass that replaces a collection of global constants + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(_sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + members = dict((name, value) for name, value in source.items() if filter(name)) + cls = cls(name, members, module=module) + cls.__reduce_ex__ = _reduce_ex_by_name + module_globals.update(cls.__members__) + module_globals[name] = cls + return cls +temp_enum_dict['_convert'] = _convert +del _convert + +Enum = EnumMeta('Enum', (object, ), temp_enum_dict) +del temp_enum_dict + +# Enum has now been created +########################### + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" + +def _reduce_ex_by_name(self, proto): + return self.name + +def unique(enumeration): + """Class decorator that ensures only unique members exist in an enumeration.""" + duplicates = [] + for name, member in enumeration.__members__.items(): + if name != member.name: + duplicates.append((name, member.name)) + if duplicates: + duplicate_names = ', '.join( + ["%s -> %s" % (alias, name) for (alias, name) in duplicates] + ) + raise ValueError('duplicate names found in %r: %s' % + (enumeration, duplicate_names) + ) + return enumeration
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 4d159e3..c37b5c0 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -8885,6 +8885,8 @@ <enum name="CrosDictationToggleDictationMethod"> <int value="0" label="Search+D"/> <int value="1" label="Click onscreen button"/> + <int value="2" label="Select the button in the SwitchAccess context menu"/> + <int value="3" label="ChromeVox gesture"/> </enum> <enum name="CrosDisksArchiveType"> @@ -9845,6 +9847,9 @@ </enum> <enum name="DataReductionProxyProtocolNotAcceptingTransformReason"> + <obsolete> + Deprecated 10/2018. + </obsolete> <int value="0" label="Not accepting proxy transforms, transforms disabled on client"/> <int value="1" @@ -16910,6 +16915,7 @@ <int value="1281" label="AUTOTESTPRIVATE_BOOTSTRAPMACHINELEARNINGSERVICE"/> <int value="1282" label="AUTOTESTPRIVATE_RUNCROSTINIUNINSTALLER"/> <int value="1283" label="AUTOTESTPRIVATE_TAKESCREENSHOT"/> + <int value="1284" label="ACCESSIBILITY_PRIVATE_TOGGLEDICTATION"/> </enum> <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 1021101..22ff1f7 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -17527,6 +17527,9 @@ <histogram name="DataReductionProxy.Protocol.NotAcceptingTransform" enum="DataReductionProxyProtocolNotAcceptingTransformReason"> + <obsolete> + Obsolete as of 10/2018. + </obsolete> <owner>dougarnett@chromium.org</owner> <summary> Records the reason that a page request is not accepting proxy server
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index b56731a..21dc50d 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -2937,6 +2937,12 @@ meaningful input with longest queuing delay per navigation. In ms. </summary> </metric> + <metric name="Navigation.PageEndReason"> + <summary> + The |page_load_metrics::PageEndReason| for the main frame navigation of + this page load. + </summary> + </metric> <metric name="Navigation.PageTransition"> <summary> The |ui::PageTransition| for the main frame navigation of this page load.
diff --git a/tools/perf/contrib/blink_layoutng_perf/loading_layout_ng.py b/tools/perf/contrib/blink_layoutng_perf/loading_layout_ng.py new file mode 100644 index 0000000..a85da250 --- /dev/null +++ b/tools/perf/contrib/blink_layoutng_perf/loading_layout_ng.py
@@ -0,0 +1,34 @@ +# 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. +from benchmarks import loading +from telemetry import benchmark + +# pylint: disable=protected-access +@benchmark.Info(emails=['cbiesinger@chromium.org'], + documentation_url='https://bit.ly/loading-benchmarks') +class LoadingDesktopLayoutNg(loading.LoadingDesktop): + """A benchmark that runs loading.desktop with the layoutng flag.""" + + def SetExtraBrowserOptions(self, options): + super(LoadingDesktopLayoutNg, self).SetExtraBrowserOptions(options) + options.AppendExtraBrowserArgs('--enable-blink-features=LayoutNG') + + @classmethod + def Name(cls): + return 'loading.desktop_layout_ng' + + +# pylint: disable=protected-access +@benchmark.Info(emails=['cbiesinger@chromium.org'], + documentation_url='https://bit.ly/loading-benchmarks') +class LoadingMobileLayoutNg(loading.LoadingDesktop): + """A benchmark that runs loading.mobile with the layoutng flag.""" + + def SetExtraBrowserOptions(self, options): + super(LoadingMobileLayoutNg, self).SetExtraBrowserOptions(options) + options.AppendExtraBrowserArgs('--enable-blink-features=LayoutNG') + + @classmethod + def Name(cls): + return 'loading.mobile_layout_ng'
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config index 737d51c..89ec212 100644 --- a/tools/perf/expectations.config +++ b/tools/perf/expectations.config
@@ -26,6 +26,9 @@ crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-sub-texture.html [ Skip ] crbug.com/853738 [ Android_Webview ] blink_perf.canvas/upload-video-to-texture.html [ Skip ] +# Benchmark: blink_perf.css +crbug.com/891878 [ Nexus5X_Webview ] blink_perf.css/CustomPropertiesVarAlias.html [ Skip ] + # Benchmark: blink_perf.layout crbug.com/551950 [ Android_Svelte ] blink_perf.layout/* [ Skip ] crbug.com/832686 [ Nexus_5 ] blink_perf.layout/subtree-detaching.html [ Skip ]
diff --git a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java index 6e53e3f..2f0465f 100644 --- a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java +++ b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
@@ -9,7 +9,9 @@ import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnLayoutChangeListener; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; import android.widget.FrameLayout; @@ -258,6 +260,16 @@ */ private int measureContentWidth() { assert mAdapter != null : "Set the adapter before showing the popup."; - return UiUtils.computeMaxWidthOfListAdapterItems(mAdapter); + int adapterWidth = UiUtils.computeMaxWidthOfListAdapterItems(mAdapter); + if (mFooterView.getChildCount() > 0) { + if (mFooterView.getLayoutParams() == null) { + mFooterView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + } + int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + mFooterView.measure(measureSpec, measureSpec); + return Math.max(mFooterView.getMeasuredWidth(), adapterWidth); + } + return adapterWidth; } }
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc index 86b08db..ee72e00 100644 --- a/ui/aura/gestures/gesture_recognizer_unittest.cc +++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -4705,8 +4705,7 @@ // Transfer event sequence from previous window to the new window. aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo( - window_1.get(), window_2.get(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + window_1.get(), window_2.get(), ui::TransferTouchesBehavior::kDontCancel); delegate_1->Reset(); delegate_1->ReceivedAck();
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index ee659a97..9a15d00 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -170,7 +170,9 @@ "event_targeter.h", "event_utils.h", "events_export.h", + "gestures/gesture_recognizer.h", "gestures/gesture_recognizer_impl_mac.h", + "gestures/gesture_recognizer_observer.h", "gestures/gesture_types.h", "keyboard_hook.h", "null_event_targeter.h", @@ -193,7 +195,9 @@ "event_target.cc", "event_utils.cc", "events_stub.cc", + "gestures/gesture_recognizer.cc", "gestures/gesture_recognizer_impl_mac.cc", + "gestures/gesture_recognizer_observer.cc", "gestures/gesture_types.cc", "keyboard_hook_base.cc", "keyboard_hook_base.h", @@ -270,7 +274,6 @@ if (use_aura) { public += [ "gestures/gesture_provider_aura.h", - "gestures/gesture_recognizer.h", "gestures/gesture_recognizer_impl.h", "gestures/motion_event_aura.h", ] @@ -592,6 +595,7 @@ if (use_aura) { sources += [ "gestures/gesture_provider_aura_unittest.cc", + "gestures/gesture_recognizer_impl_unittest.cc", "gestures/motion_event_aura_unittest.cc", ] }
diff --git a/ui/events/gestures/gesture_recognizer.cc b/ui/events/gestures/gesture_recognizer.cc new file mode 100644 index 0000000..533ab77 --- /dev/null +++ b/ui/events/gestures/gesture_recognizer.cc
@@ -0,0 +1,22 @@ +// 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 "ui/events/gestures/gesture_recognizer.h" + +#include "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +GestureRecognizer::GestureRecognizer() = default; +GestureRecognizer::~GestureRecognizer() = default; + +void GestureRecognizer::AddObserver(GestureRecognizerObserver* observer) { + observers_.AddObserver(observer); +} + +void GestureRecognizer::RemoveObserver(GestureRecognizerObserver* observer) { + observers_.RemoveObserver(observer); +} + +} // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer.h b/ui/events/gestures/gesture_recognizer.h index f4da6a0..de966ee 100644 --- a/ui/events/gestures/gesture_recognizer.h +++ b/ui/events/gestures/gesture_recognizer.h
@@ -10,19 +10,23 @@ #include <memory> #include <vector> +#include "base/observer_list.h" #include "ui/events/event_constants.h" #include "ui/events/events_export.h" #include "ui/events/gestures/gesture_types.h" #include "ui/gfx/geometry/point_f.h" namespace ui { +class GestureRecognizerObserver; + // A GestureRecognizer is an abstract base class for conversion of touch events // into gestures. class EVENTS_EXPORT GestureRecognizer { public: using Gestures = std::vector<std::unique_ptr<GestureEvent>>; - virtual ~GestureRecognizer() {} + GestureRecognizer(); + virtual ~GestureRecognizer(); // Invoked before event dispatch. If the event is invalid given the current // touch sequence, returns false. @@ -57,15 +61,14 @@ // |not_cancelled| == nullptr, cancels all touches. virtual void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) = 0; - enum class ShouldCancelTouches { Cancel, DontCancel }; - // Transfer the gesture stream from the drag source (current_consumer) to the - // consumer used for dragging (new_consumer). If |should_cancel_touches| is - // Cancel, dispatches cancel events to |current_consumer| to ensure that its - // touch stream remains valid. - virtual void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) = 0; + // consumer used for dragging (new_consumer). If |transfer_touches_behavior| + // is kCancel, dispatches cancel events to |current_consumer| to ensure that + // its touch stream remains valid. + virtual void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) = 0; // If a gesture is underway for |consumer| |point| is set to the last touch // point and true is returned. If no touch events have been processed for @@ -87,6 +90,19 @@ // Since the GestureRecognizer does not own the |helper|, it is not deleted // and must be cleaned up appropriately by the caller. virtual void RemoveGestureEventHelper(GestureEventHelper* helper) = 0; + + void AddObserver(GestureRecognizerObserver* observer); + void RemoveObserver(GestureRecognizerObserver* observer); + + protected: + const base::ObserverList<GestureRecognizerObserver>& observers() { + return observers_; + } + + private: + base::ObserverList<GestureRecognizerObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(GestureRecognizer); }; } // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc index 1702be9..2b3925d4 100644 --- a/ui/events/gestures/gesture_recognizer_impl.cc +++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -19,6 +19,7 @@ #include "ui/events/event_switches.h" #include "ui/events/event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" +#include "ui/events/gestures/gesture_recognizer_observer.h" #include "ui/events/gestures/gesture_types.h" namespace ui { @@ -107,33 +108,21 @@ void GestureRecognizerImpl::CancelActiveTouchesExcept( GestureConsumer* not_cancelled) { - // Do not iterate directly over |consumer_gesture_provider_| because canceling - // active touches may cause the consumer to be removed from - // |consumer_gesture_provider_|. See crbug.com/651258 for more info. - std::vector<GestureConsumer*> consumers(consumer_gesture_provider_.size()); - for (const auto& entry : consumer_gesture_provider_) { - if (entry.first == not_cancelled) - continue; - - consumers.push_back(entry.first); - } - - for (auto* consumer : consumers) - CancelActiveTouches(consumer); + CancelActiveTouchesExceptImpl(not_cancelled, kNotifyObservers); } void GestureRecognizerImpl::TransferEventsTo( GestureConsumer* current_consumer, GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) { + TransferTouchesBehavior transfer_touches_behavior) { // This method transfers the gesture stream from |current_consumer| to - // |new_consumer|. If |should_cancel_touches| is Cancel, it ensures that both - // consumers retain a touch event stream which is reasonably valid. In order - // to do this we + // |new_consumer|. If |transfer_touches_behavior| is kCancel, it ensures that + // both consumers retain a touch event stream which is reasonably valid. In + // order to do this we // - record what pointers are currently down on |current_consumer| // - cancel touches on consumers other than |current_consumer| // - move the gesture provider from |current_consumer| to |new_consumer| - // - if |should_cancel_touches| + // - if |transfer_touches_behavior| is kCancel // - synchronize the state of the new gesture provider associated with // current_consumer with with the touch state of the consumer itself via // OnTouchEnter. @@ -153,7 +142,7 @@ touchids_targeted_at_current.push_back(touch_id_target.first); } - CancelActiveTouchesExcept(current_consumer); + CancelActiveTouchesExceptImpl(current_consumer, kDontNotifyObservers); std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = GetEventPerPointForConsumer(current_consumer, ET_TOUCH_CANCELLED); @@ -164,9 +153,7 @@ // but has some pointers down which need cancelling. In order to ensure that // the GR sees a valid event stream, inform it of these pointers via // OnTouchEnter, and then synthesize a touch cancel per pointer. - if (should_cancel_touches == - GestureRecognizer::ShouldCancelTouches::Cancel && - helper) { + if (transfer_touches_behavior == TransferTouchesBehavior::kCancel && helper) { GestureProviderAura* gesture_provider = GetGestureProviderForConsumer(current_consumer); @@ -179,6 +166,9 @@ for (int touch_id : touchids_targeted_at_current) touch_id_target_[touch_id] = new_consumer; + for (GestureRecognizerObserver& observer : observers()) + observer.OnEventsTransferred(current_consumer, new_consumer, + transfer_touches_behavior); } bool GestureRecognizerImpl::GetLastTouchPointForTarget( @@ -224,21 +214,11 @@ } bool GestureRecognizerImpl::CancelActiveTouches(GestureConsumer* consumer) { - GestureEventHelper* helper = - FindDispatchHelperForConsumer(consumer); - - if (!helper) - return false; - - std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = - GetEventPerPointForConsumer(consumer, ET_TOUCH_CANCELLED); - for (const std::unique_ptr<TouchEvent>& cancelling_touch : cancelling_touches) - helper->DispatchSyntheticTouchEvent(cancelling_touch.get()); - return cancelling_touches.size() > 0U; + return CancelActiveTouchesImpl(consumer, kNotifyObservers); } //////////////////////////////////////////////////////////////////////////////// -// GestureRecognizerImpl, private: +// GestureRecognizerImpl, protected: GestureProviderAura* GestureRecognizerImpl::GetGestureProviderForConsumer( GestureConsumer* consumer) { @@ -256,6 +236,22 @@ return gesture_provider; } +bool GestureRecognizerImpl::ProcessTouchEventPreDispatch( + TouchEvent* event, + GestureConsumer* consumer) { + SetupTargets(*event, consumer); + + if (event->result() & ER_CONSUMED) + return false; + + GestureProviderAura* gesture_provider = + GetGestureProviderForConsumer(consumer); + return gesture_provider->OnTouchEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////// +// GestureRecognizerImpl, private: + void GestureRecognizerImpl::SetupTargets(const TouchEvent& event, GestureConsumer* target) { event_to_gesture_provider_[event.unique_event_id()] = @@ -279,19 +275,6 @@ } } -bool GestureRecognizerImpl::ProcessTouchEventPreDispatch( - TouchEvent* event, - GestureConsumer* consumer) { - SetupTargets(*event, consumer); - - if (event->result() & ER_CONSUMED) - return false; - - GestureProviderAura* gesture_provider = - GetGestureProviderForConsumer(consumer); - return gesture_provider->OnTouchEvent(event); -} - GestureRecognizer::Gestures GestureRecognizerImpl::AckTouchEvent( uint32_t unique_event_id, ui::EventResult result, @@ -314,6 +297,48 @@ return gesture_provider->GetAndResetPendingGestures(); } +void GestureRecognizerImpl::CancelActiveTouchesExceptImpl( + GestureConsumer* not_cancelled, + ShouldNotifyObservers should_notify) { + // Do not iterate directly over |consumer_gesture_provider_| because canceling + // active touches may cause the consumer to be removed from + // |consumer_gesture_provider_|. See https://crbug.com/651258 for more info. + std::vector<GestureConsumer*> consumers(consumer_gesture_provider_.size()); + for (const auto& entry : consumer_gesture_provider_) { + if (entry.first == not_cancelled) + continue; + + consumers.push_back(entry.first); + } + + for (auto* consumer : consumers) + CancelActiveTouchesImpl(consumer, kDontNotifyObservers); + + if (should_notify == kDontNotifyObservers) + return; + for (GestureRecognizerObserver& observer : observers()) + observer.OnActiveTouchesCanceledExcept(not_cancelled); +} + +bool GestureRecognizerImpl::CancelActiveTouchesImpl( + GestureConsumer* consumer, + ShouldNotifyObservers should_notify) { + GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer); + + if (!helper) + return false; + + std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = + GetEventPerPointForConsumer(consumer, ET_TOUCH_CANCELLED); + for (const std::unique_ptr<TouchEvent>& cancelling_touch : cancelling_touches) + helper->DispatchSyntheticTouchEvent(cancelling_touch.get()); + if (should_notify == kNotifyObservers) { + for (GestureRecognizerObserver& observer : observers()) + observer.OnActiveTouchesCanceled(consumer); + } + return !cancelling_touches.empty(); +} + bool GestureRecognizerImpl::CleanupStateForConsumer( GestureConsumer* consumer) { bool state_cleaned_up = false;
diff --git a/ui/events/gestures/gesture_recognizer_impl.h b/ui/events/gestures/gesture_recognizer_impl.h index ba6c416..36e8fc9 100644 --- a/ui/events/gestures/gesture_recognizer_impl.h +++ b/ui/events/gestures/gesture_recognizer_impl.h
@@ -48,9 +48,10 @@ GestureConsumer* GetTargetForLocation(const gfx::PointF& location, int source_device_id) override; void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override; - void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) override; + void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override; bool GetLastTouchPointForTarget(GestureConsumer* consumer, gfx::PointF* point) override; bool CancelActiveTouches(GestureConsumer* consumer) override; @@ -64,6 +65,8 @@ GestureConsumer* consumer) override; private: + enum ShouldNotifyObservers { kNotifyObservers, kDontNotifyObservers }; + // Sets up the target consumer for gestures based on the touch-event. void SetupTargets(const TouchEvent& event, GestureConsumer* consumer); @@ -75,6 +78,11 @@ bool is_source_touch_event_set_non_blocking, GestureConsumer* consumer) override; + void CancelActiveTouchesExceptImpl(GestureConsumer* not_cancelled, + ShouldNotifyObservers should_notify); + bool CancelActiveTouchesImpl(GestureConsumer* consumer, + ShouldNotifyObservers should_notify); + bool CleanupStateForConsumer(GestureConsumer* consumer) override; void AddGestureEventHelper(GestureEventHelper* helper) override; void RemoveGestureEventHelper(GestureEventHelper* helper) override;
diff --git a/ui/events/gestures/gesture_recognizer_impl_mac.cc b/ui/events/gestures/gesture_recognizer_impl_mac.cc index b52a8db..d66805df 100644 --- a/ui/events/gestures/gesture_recognizer_impl_mac.cc +++ b/ui/events/gestures/gesture_recognizer_impl_mac.cc
@@ -45,7 +45,7 @@ void GestureRecognizerImplMac::TransferEventsTo( GestureConsumer* current_consumer, GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) {} + TransferTouchesBehavior transfer_touches_behavior) {} bool GestureRecognizerImplMac::GetLastTouchPointForTarget( GestureConsumer* consumer,
diff --git a/ui/events/gestures/gesture_recognizer_impl_mac.h b/ui/events/gestures/gesture_recognizer_impl_mac.h index 391bd2b..f0c4615f 100644 --- a/ui/events/gestures/gesture_recognizer_impl_mac.h +++ b/ui/events/gestures/gesture_recognizer_impl_mac.h
@@ -34,9 +34,10 @@ GestureConsumer* GetTargetForLocation(const gfx::PointF& location, int source_device_id) override; void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override; - void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) override; + void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override; bool GetLastTouchPointForTarget(GestureConsumer* consumer, gfx::PointF* point) override; bool CancelActiveTouches(GestureConsumer* consumer) override;
diff --git a/ui/events/gestures/gesture_recognizer_impl_unittest.cc b/ui/events/gestures/gesture_recognizer_impl_unittest.cc new file mode 100644 index 0000000..6446df7 --- /dev/null +++ b/ui/events/gestures/gesture_recognizer_impl_unittest.cc
@@ -0,0 +1,163 @@ +// 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 "ui/events/gestures/gesture_recognizer_impl.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +class TestGestureRecognizerObserver : public GestureRecognizerObserver { + public: + TestGestureRecognizerObserver(GestureRecognizer* gesture_recognizer) + : gesture_recognizer_(gesture_recognizer) { + gesture_recognizer_->AddObserver(this); + } + + ~TestGestureRecognizerObserver() override { + gesture_recognizer_->RemoveObserver(this); + } + + int active_touches_cancelled_except_call_count() const { + return active_touches_cancelled_except_call_count_; + } + int active_touches_cancelled_call_count() const { + return active_touches_cancelled_call_count_; + } + int events_transfer_call_count() const { return events_transfer_call_count_; } + + const GestureConsumer* last_not_cancelled() const { + return last_not_cancelled_; + } + const GestureConsumer* last_cancelled() const { + return last_cancelled_consumer_; + } + + const GestureConsumer* last_transferred_source() const { + return last_transferred_source_; + } + const GestureConsumer* last_transferred_destination() const { + return last_transferred_destination_; + } + TransferTouchesBehavior last_transfer_touches_behavior() const { + return last_transfer_touches_behavior_; + } + + private: + // GestureRecognizerObserver: + void OnActiveTouchesCanceledExcept(GestureConsumer* not_cancelled) override { + active_touches_cancelled_except_call_count_++; + last_not_cancelled_ = not_cancelled; + } + void OnEventsTransferred( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override { + events_transfer_call_count_++; + last_transferred_source_ = current_consumer; + last_transferred_destination_ = new_consumer; + last_transfer_touches_behavior_ = transfer_touches_behavior; + } + void OnActiveTouchesCanceled(GestureConsumer* consumer) override { + active_touches_cancelled_call_count_++; + last_cancelled_consumer_ = consumer; + } + + GestureRecognizer* gesture_recognizer_; + + int active_touches_cancelled_except_call_count_ = 0; + int active_touches_cancelled_call_count_ = 0; + int events_transfer_call_count_ = 0; + GestureConsumer* last_not_cancelled_ = nullptr; + GestureConsumer* last_cancelled_consumer_ = nullptr; + GestureConsumer* last_transferred_source_ = nullptr; + GestureConsumer* last_transferred_destination_ = nullptr; + TransferTouchesBehavior last_transfer_touches_behavior_ = + TransferTouchesBehavior::kCancel; + + DISALLOW_COPY_AND_ASSIGN(TestGestureRecognizerObserver); +}; + +class TestGestureEventHelper : public GestureEventHelper { + public: + TestGestureEventHelper() = default; + ~TestGestureEventHelper() override = default; + + private: + // GestureEventHelper: + bool CanDispatchToConsumer(GestureConsumer* consumer) override { + return true; + } + void DispatchGestureEvent(GestureConsumer* raw_input_consumer, + GestureEvent* event) override {} + void DispatchSyntheticTouchEvent(TouchEvent* event) override {} + + DISALLOW_COPY_AND_ASSIGN(TestGestureEventHelper); +}; + +class GestureRecognizerImplTest : public testing::Test { + public: + GestureRecognizerImplTest() = default; + ~GestureRecognizerImplTest() override = default; + + void SetUp() override { + gesture_recognizer_.helpers().push_back(&helper_); + observer_ = + std::make_unique<TestGestureRecognizerObserver>(&gesture_recognizer_); + } + + void TearDown() override { observer_.reset(); } + + protected: + GestureRecognizer* gesture_recognizer() { return &gesture_recognizer_; } + TestGestureRecognizerObserver* observer() { return observer_.get(); } + + private: + GestureRecognizerImpl gesture_recognizer_; + TestGestureEventHelper helper_; + std::unique_ptr<TestGestureRecognizerObserver> observer_; + + DISALLOW_COPY_AND_ASSIGN(GestureRecognizerImplTest); +}; + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEvents) { + GestureConsumer consumer; + gesture_recognizer()->CancelActiveTouches(&consumer); + + EXPECT_EQ(1, observer()->active_touches_cancelled_call_count()); + EXPECT_EQ(&consumer, observer()->last_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEventsExcept) { + GestureConsumer consumer; + gesture_recognizer()->CancelActiveTouchesExcept(&consumer); + + // OnActiveTouchesCancelled() shouldn't occur. + EXPECT_EQ(0, observer()->active_touches_cancelled_call_count()); + EXPECT_EQ(1, observer()->active_touches_cancelled_except_call_count()); + EXPECT_EQ(&consumer, observer()->last_not_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEventsExceptNullPtr) { + gesture_recognizer()->CancelActiveTouchesExcept(nullptr); + + EXPECT_EQ(1, observer()->active_touches_cancelled_except_call_count()); + EXPECT_FALSE(observer()->last_not_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, TransferEventsTo) { + GestureConsumer consumer1; + GestureConsumer consumer2; + + gesture_recognizer()->TransferEventsTo(&consumer1, &consumer2, + TransferTouchesBehavior::kDontCancel); + EXPECT_EQ(1, observer()->events_transfer_call_count()); + EXPECT_EQ(&consumer1, observer()->last_transferred_source()); + EXPECT_EQ(&consumer2, observer()->last_transferred_destination()); + EXPECT_EQ(TransferTouchesBehavior::kDontCancel, + observer()->last_transfer_touches_behavior()); +} + +} // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_observer.cc b/ui/events/gestures/gesture_recognizer_observer.cc new file mode 100644 index 0000000..4430530 --- /dev/null +++ b/ui/events/gestures/gesture_recognizer_observer.cc
@@ -0,0 +1,11 @@ +// 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 "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +GestureRecognizerObserver::~GestureRecognizerObserver() = default; + +} // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_observer.h b/ui/events/gestures/gesture_recognizer_observer.h new file mode 100644 index 0000000..4826cda5 --- /dev/null +++ b/ui/events/gestures/gesture_recognizer_observer.h
@@ -0,0 +1,30 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_ +#define UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_ + +#include "base/observer_list_types.h" +#include "ui/events/events_export.h" +#include "ui/events/gestures/gesture_types.h" + +namespace ui { + +class EVENTS_EXPORT GestureRecognizerObserver : public base::CheckedObserver { + public: + virtual void OnActiveTouchesCanceledExcept( + GestureConsumer* not_cancelled) = 0; + virtual void OnEventsTransferred( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) = 0; + virtual void OnActiveTouchesCanceled(GestureConsumer* consumer) = 0; + + protected: + ~GestureRecognizerObserver() override; +}; + +} // namespace ui + +#endif // UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_
diff --git a/ui/events/gestures/gesture_types.h b/ui/events/gestures/gesture_types.h index 3d63c57..e233411 100644 --- a/ui/events/gestures/gesture_types.h +++ b/ui/events/gestures/gesture_types.h
@@ -12,6 +12,17 @@ class GestureEvent; class TouchEvent; +// TransferTouchesBehavior customizes the behavior of +// GestureRecognizer::TransferEventsTo. +enum class TransferTouchesBehavior { + // Dispatches the cancel events to the current consumer on transfer to ensure + // its touch stream remains valid. + kCancel, + + // Do not dispatch cancel events. + kDontCancel +}; + // An abstract type for consumers of gesture-events created by the // gesture-recognizer. class EVENTS_EXPORT GestureConsumer {
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc index 104ae4c6..780a0f9 100644 --- a/ui/views/controls/menu/menu_host.cc +++ b/ui/views/controls/menu/menu_host.cc
@@ -86,7 +86,7 @@ #else // !defined(OS_MACOSX) source->GetGestureRecognizer()->TransferEventsTo( source->GetNativeView(), target->GetNativeView(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + ui::TransferTouchesBehavior::kDontCancel); #endif // defined(OS_MACOSX) }
diff --git a/ui/views_bridge_mac/bridged_content_view.mm b/ui/views_bridge_mac/bridged_content_view.mm index d4115a0..100d99e 100644 --- a/ui/views_bridge_mac/bridged_content_view.mm +++ b/ui/views_bridge_mac/bridged_content_view.mm
@@ -195,6 +195,8 @@ return ui::TextEditCommand::COPY; if (action == @selector(paste:)) return ui::TextEditCommand::PASTE; + if (action == @selector(pasteAndMatchStyle:)) + return ui::TextEditCommand::PASTE; if (action == @selector(selectAll:)) return ui::TextEditCommand::SELECT_ALL; return ui::TextEditCommand::INVALID_COMMAND; @@ -260,6 +262,7 @@ - (void)cut:(id)sender; - (void)copy:(id)sender; - (void)paste:(id)sender; +- (void)pasteAndMatchStyle:(id)sender; - (void)selectAll:(id)sender; @end @@ -660,6 +663,13 @@ eventFlags:ui::EF_CONTROL_DOWN]; } +- (void)pasteAndMatchStyle:(id)sender { + [self handleAction:ui::TextEditCommand::PASTE + keyCode:ui::VKEY_V + domCode:ui::DomCode::US_V + eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; +} + - (void)selectAll:(id)sender { [self handleAction:ui::TextEditCommand::SELECT_ALL keyCode:ui::VKEY_A @@ -855,6 +865,13 @@ } - (void)flagsChanged:(NSEvent*)theEvent { + if (theEvent.keyCode == 0) { + // An event like this gets sent when sending some key commands via + // AppleScript. Since 0 is VKEY_A, we end up interpreting this as Cmd+A + // which is incorrect. The correct event for command up/down (keyCode = 55) + // is also sent, so we should drop this one. See https://crbug.com/889618 + return; + } ui::KeyEvent event(theEvent); [self handleKeyEvent:&event]; }