diff --git a/AUTHORS b/AUTHORS index 03f2b4b..aec4fb2 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -1029,6 +1029,7 @@ NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> Optical Tone Ltd <*@opticaltone.com> +Pengutronix e.K. <*@pengutronix.de> Rakuten Kobo Inc. <*@kobo.com> Rakuten Kobo Inc. <*@rakuten.com> Seznam.cz, a.s. <*@firma.seznam.cz>
diff --git a/DEPS b/DEPS index 6ccb663..6c2f029 100644 --- a/DEPS +++ b/DEPS
@@ -121,11 +121,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'b7af275ebbf55b6c79cf361bcf87612b328ea50a', + 'skia_revision': 'e9742a5a0db1e86f1b548d74c025404690eaed63', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'e0899ad9ee97543e6f45961fe13435882df25b8a', + 'v8_revision': '7920be1f5a1fa8e0a6484e50a5e15bb4da2f29d9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -133,7 +133,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': '783809808ec09d2040047c252925b19f507b06c8', + 'angle_revision': '794364ebd51c1c7d113fc4208857a15cd557b4db', # 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. @@ -145,7 +145,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': 'f8940afa9d5f0810481a374a5282dfa4c774eb08', + 'pdfium_revision': '96a7c58e4c7196b2f789b3e7177f580caed3417d', # 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. @@ -179,6 +179,10 @@ # and whatever else without interference from each other. 'harfbuzz_revision': 'fe532923101586e316b300d419a337d357cd93da', # Three lines of non-changing comments so that + # the commit queue can handle CLs rolling Emoji Segmenter + # and whatever else without interference from each other. + 'emoji_segmenter_revision': '9ba6d25d0d9313569665d4a9d2b34f0f39f9a50e', + # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. 'catapult_revision': '2061050c4d27b3b27bc3cfc7cf20fc99644f66f7', @@ -197,7 +201,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': 'ed7c2ae304814efdb0e85cfbfe5205897b0d8d03', + 'feed_revision': '25362a7395755d79755a83ebb5eb19ec6ad0af20', # 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. @@ -690,7 +694,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2e223a915e69248a5edc320d032731e16448d6fc', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'baf62c129325a527911fa12a625498ecb5cd39a8', 'condition': 'checkout_linux', }, @@ -715,7 +719,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1131ccb69495f362b169f663f276b92a6042d1a6', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '50b187ca83716db937923d9ed1ef031f6e071d2a', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -770,6 +774,9 @@ 'src/third_party/harfbuzz-ng/src': Var('chromium_git') + '/external/github.com/harfbuzz/harfbuzz.git' + '@' + Var('harfbuzz_revision'), + 'src/third_party/emoji-segmenter/src/': + Var('chromium_git') + '/external/github.com/googlei18n/emoji-segmenter.git' + '@' + Var('emoji_segmenter_revision'), + # Chrome OS touchpad gestures library. 'src/third_party/gestures/gestures': { 'url': Var('chromium_git') + '/chromiumos/platform/gestures.git' + '@' + '74f55100df966280d305d5d5ada824605f875839', @@ -1251,7 +1258,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c8eab41c449dab4ab19b8699b3fca179a40ec371', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cad0b15c2af9195854eb6b0122d841c0deb66b5d', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java index 6f4f333..3057d796 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTest.java
@@ -36,35 +36,28 @@ public void testModeBrowser() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode( - "browser", false, "native-include-thread-names", true, false, false)); + Assert.assertTrue( + shim.runTestForMode("browser", false, "native-include-thread-names", false, false)); } @Test @MediumTest public void testModeBrowserDynamicPseudo() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false, false)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false)); } @Test @MediumTest public void testModeBrowserDynamicPseudoSampleEverything() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, true)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true)); } @Test @MediumTest public void testModeBrowserDynamicPseudoSamplePartial() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, false)); - } - - @Test - @MediumTest - public void testModeBrowserDynamicPseudoSamplePartialNonStreaming() throws Exception { - HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, true, false)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false)); } }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java index 6a3f052..7403f6b5 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/HeapProfilingTestShim.java
@@ -24,9 +24,9 @@ * rather than native stacks. */ public boolean runTestForMode(String mode, boolean dynamicallyStartProfiling, String stackMode, - boolean streamSamples, boolean shouldSample, boolean sampleEverything) { + boolean shouldSample, boolean sampleEverything) { return nativeRunTestForMode(mNativeHeapProfilingTestShim, mode, dynamicallyStartProfiling, - stackMode, streamSamples, shouldSample, sampleEverything); + stackMode, shouldSample, sampleEverything); } /** @@ -44,6 +44,6 @@ private native long nativeInit(); private native void nativeDestroy(long nativeHeapProfilingTestShim); private native boolean nativeRunTestForMode(long nativeHeapProfilingTestShim, String mode, - boolean dynamicallyStartProfiling, String stackMode, boolean streamSamples, - boolean shouldSample, boolean sampleEverything); + boolean dynamicallyStartProfiling, String stackMode, boolean shouldSample, + boolean sampleEverything); }
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc index 14f20a5..eac17086 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -325,11 +325,6 @@ TLSSetValue(g_internal_reentry_guard, false); } -// static -bool PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted() { - return TLSGetValue(g_internal_reentry_guard); -} - PoissonAllocationSampler* PoissonAllocationSampler::instance_; PoissonAllocationSampler::PoissonAllocationSampler() {
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.h b/base/sampling_heap_profiler/poisson_allocation_sampler.h index 0dfb8b2a8..a55fdea 100644 --- a/base/sampling_heap_profiler/poisson_allocation_sampler.h +++ b/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -54,8 +54,6 @@ public: ScopedMuteThreadSamples(); ~ScopedMuteThreadSamples(); - - static bool IsMuted(); }; // Must be called early during the process initialization. It creates and
diff --git a/chrome/VERSION b/chrome/VERSION index f2692f14..7f4cf97 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=74 MINOR=0 -BUILD=3689 +BUILD=3690 PATCH=0
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java index c17238e7..398085e3 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedLoggingBridge.java
@@ -17,6 +17,7 @@ import org.chromium.chrome.browser.profiles.Profile; import org.chromium.ui.mojom.WindowOpenDisposition; +import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -198,6 +199,11 @@ nativeOnSpinnerShown(mNativeFeedLoggingBridge, timeShownMs); } + @Override + public void onPietFrameRenderingEvent(List<Integer> pietErrorCodes) { + // TODO(https://crbug.com/924739): Implementation. + } + /** * Reports how long a user spends on the page. *
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 9ec78ee..eea9faee 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -740,16 +740,6 @@ </activity> {% endfor %} - <activity android:name="org.chromium.chrome.browser.media.remote.ExpandedControllerActivity" - android:theme="@style/MainTheme" - android:label="Chrome.ExpandedControllerActivity" - android:hardwareAccelerated="true" - android:launchMode="singleTask" - android:noHistory="true" - android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" - android:excludeFromRecents="true"> - </activity> - <activity android:name="org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity" android:theme="@style/MainTheme" android:label="Chrome.CafExpandedControllerActivity"
diff --git a/chrome/android/java/monochrome_public_apk.AndroidManifest.expected b/chrome/android/java/monochrome_public_apk.AndroidManifest.expected index 0ce918f..0a4d36b 100644 --- a/chrome/android/java/monochrome_public_apk.AndroidManifest.expected +++ b/chrome/android/java/monochrome_public_apk.AndroidManifest.expected
@@ -1333,16 +1333,6 @@ </intent-filter> </activity> <activity - android:name="org.chromium.chrome.browser.media.remote.ExpandedControllerActivity" - android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" - android:excludeFromRecents="true" - android:hardwareAccelerated="true" - android:label="Chrome.ExpandedControllerActivity" - android:launchMode="singleTask" - android:noHistory="true" - android:theme="@style/MainTheme" > - </activity> - <activity android:name="org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" android:excludeFromRecents="true"
diff --git a/chrome/android/java/res/layout/cast_controller_media_route_button.xml b/chrome/android/java/res/layout/cast_controller_media_route_button.xml deleted file mode 100644 index 547361b..0000000 --- a/chrome/android/java/res/layout/cast_controller_media_route_button.xml +++ /dev/null
@@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<org.chromium.chrome.browser.media.remote.FullscreenMediaRouteButton - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/cast_controller_media_route_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="visible" - android:layout_gravity = "start|top" - style="@style/CastMediaRouteButton" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index 6b85d55..29d16b6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -167,7 +167,6 @@ public static final String AUTOFILL_ASSISTANT = "AutofillAssistant"; public static final String AUTOFILL_REFRESH_STYLE_ANDROID = "AutofillRefreshStyleAndroid"; public static final String AUTOFILL_KEYBOARD_ACCESSORY = "AutofillKeyboardAccessory"; - public static final String CAF_MEDIA_ROUTER_IMPL = "CafMediaRouterImpl"; public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList"; public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab"; public static final String CCT_MODULE = "CCTModule";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteController.java deleted file mode 100644 index bedcd3b958..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteController.java +++ /dev/null
@@ -1,658 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import android.content.Context; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Handler; -import android.os.SystemClock; -import android.support.annotation.Nullable; -import android.support.v7.media.MediaControlIntent; -import android.support.v7.media.MediaItemStatus; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; - -import com.google.android.gms.cast.CastMediaControlIntent; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.RemovableInRelease; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.ui.widget.Toast; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; - -/** - * Class containing the common, connection type independent, code for all MediaRouteControllers. - */ -public abstract class AbstractMediaRouteController implements MediaRouteController { - - /** - * Callback class for monitoring whether any routes exist, and hence deciding whether to show - * the cast UI to users. - */ - private class DeviceDiscoveryCallback extends MediaRouter.Callback { - @Override - public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) { - updateRouteAvailability(); - } - - @Override - public void onProviderChanged( - MediaRouter router, MediaRouter.ProviderInfo provider) { - updateRouteAvailability(); - } - - @Override - public void onProviderRemoved( - MediaRouter router, MediaRouter.ProviderInfo provider) { - updateRouteAvailability(); - } - - @Override - public void onRouteAdded(MediaRouter router, RouteInfo route) { - logRoute("Added route", route); - updateRouteAvailability(); - } - - @Override - public void onRouteRemoved(MediaRouter router, RouteInfo route) { - logRoute("Removed route", route); - updateRouteAvailability(); - } - - @Override - public void onRouteChanged(MediaRouter router, RouteInfo route) { - logRoute("Changed route", route); - updateRouteAvailability(); - } - - private void updateRouteAvailability() { - if (mediaRouterInitializationFailed()) return; - - boolean routesAvailable = getMediaRouter().isRouteAvailable(mMediaRouteSelector, - MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); - if (routesAvailable != mRoutesAvailable) { - mRoutesAvailable = routesAvailable; - Log.d(TAG, "Remote media route availability changed, updating listeners"); - for (MediaStateListener listener : mAvailableRouteListeners) { - listener.onRouteAvailabilityChanged(routesAvailable); - } - } - } - } - - /** - * Callback class for monitoring whether a route has been selected, and the state of the - * selected route. - */ - private class DeviceSelectionCallback extends MediaRouter.Callback { - // Note that this doesn't use onRouteSelected, but instead casting is started directly - // by the selection dialog. It has to be done that way, since selecting the current route - // on a new video doesn't call onRouteSelected. - - private Runnable mConnectionFailureNotifier = new Runnable() { - @Override - public void run() { - release(); - mConnectionFailureNotifierQueued = false; - } - }; - - /** True if we are waiting for the MediaRouter route to connect or reconnect */ - private boolean mConnectionFailureNotifierQueued; - - private void clearConnectionFailureCallback() { - getHandler().removeCallbacks(mConnectionFailureNotifier); - mConnectionFailureNotifierQueued = false; - } - - @Override - public void onRouteChanged(MediaRouter router, RouteInfo route) { - // We only care about changes to the current route. - if (!route.equals(getCurrentRoute())) return; - // When there is no wifi connection, this condition becomes true. - if (route.isConnecting()) { - // We don't want to post the same Runnable twice. - if (!mConnectionFailureNotifierQueued) { - mConnectionFailureNotifierQueued = true; - getHandler().postDelayed(mConnectionFailureNotifier, - CONNECTION_FAILURE_NOTIFICATION_DELAY_MS); - } - } else { - // Only cancel the disconnect if we already posted the message. We can get into this - // situation if we swap the current route provider (for example, switching to a YT - // video while casting a non-YT video). - if (mConnectionFailureNotifierQueued) { - // We have reconnected, cancel the delayed disconnect. - getHandler().removeCallbacks(mConnectionFailureNotifier); - mConnectionFailureNotifierQueued = false; - } - } - } - - @Override - public void onRouteUnselected(MediaRouter router, RouteInfo route) { - onRouteUnselectedEvent(router, route); - if (getCurrentRoute() != null && !getCurrentRoute().isDefault() - && route.getId().equals(getCurrentRoute().getId())) { - RecordCastAction.castEndedTimeRemaining(getDuration(), - getDuration() - getPosition()); - release(); - } - } - } - - /** Number of ms to wait for reconnection, after which we call the failure callbacks. */ - protected static final long CONNECTION_FAILURE_NOTIFICATION_DELAY_MS = 10000L; - private static final long END_OF_VIDEO_THRESHOLD_MS = 500L; - private static final String TAG = "MediaFling"; - private final Set<MediaStateListener> mAvailableRouteListeners; - private final Context mContext; - private RouteInfo mCurrentRoute; - private final DeviceDiscoveryCallback mDeviceDiscoveryCallback;; - private final DeviceSelectionCallback mDeviceSelectionCallback; - - private final Handler mHandler; - private boolean mIsPrepared; - - private final MediaRouter mMediaRouter; - - private final MediaRouteSelector mMediaRouteSelector; - /** - * The media state listener connects to the web page that requested casting. It will be null if - * that page is no longer in a tab, but closing the page or tab should not stop cast. Cast can - * still be controlled through the notification even if the page is closed. - */ - private MediaStateListener mMediaStateListener; - - // There are times when the player state shown to user (e.g. just after pressing the pause - // button) should update before we receive an update from the Chromecast, so we have to track - // two player states. - private @PlayerState int mRemotePlayerState = PlayerState.FINISHED; - private @PlayerState int mDisplayedPlayerState = PlayerState.FINISHED; - private boolean mRoutesAvailable; - private final Set<UiListener> mUiListeners; - private boolean mWatchingRouteSelection; - - private long mMediaElementAttachedTimestampMs; - private long mMediaElementDetachedTimestampMs; - - protected AbstractMediaRouteController() { - mContext = ContextUtils.getApplicationContext(); - assert (getContext() != null); - - mHandler = new Handler(); - - mMediaRouteSelector = buildMediaRouteSelector(); - - MediaRouter mediaRouter; - - try { - // Pre-MR1 versions of JB do not have the complete MediaRouter APIs, - // so getting the MediaRouter instance will throw an exception. - mediaRouter = MediaRouter.getInstance(getContext()); - } catch (NoSuchMethodError e) { - Log.e(TAG, "Can't get an instance of MediaRouter, casting is not supported." - + " Are you still on JB (JVP15S)?"); - mediaRouter = null; - } - mMediaRouter = mediaRouter; - - mAvailableRouteListeners = new HashSet<MediaStateListener>(); - // TODO(aberent): I am unclear why this is accessed from multiple threads, but - // if I make it a HashSet then it gets ConcurrentModificationExceptions on some - // types of disconnect. Investigate and fix. - mUiListeners = new CopyOnWriteArraySet<UiListener>(); - - mDeviceDiscoveryCallback = new DeviceDiscoveryCallback(); - mDeviceSelectionCallback = new DeviceSelectionCallback(); - } - - @Override - public void addMediaStateListener(MediaStateListener listener) { - if (mediaRouterInitializationFailed()) return; - - if (mAvailableRouteListeners.isEmpty()) { - getMediaRouter().addCallback(mMediaRouteSelector, mDeviceDiscoveryCallback, - MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); - Log.d(TAG, "Started device discovery"); - - // Get the initial state - mRoutesAvailable = getMediaRouter().isRouteAvailable( - mMediaRouteSelector, MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); - } - mAvailableRouteListeners.add(listener); - // Send the current state to the listener. - listener.onRouteAvailabilityChanged(mRoutesAvailable); - } - - @Override - public void addUiListener(UiListener listener) { - mUiListeners.add(listener); - } - - protected void clearConnectionFailureCallback() { - mDeviceSelectionCallback.clearConnectionFailureCallback(); - } - - /** - * Clear the current playing item (if any) but not the associated session. - */ - protected void clearItemState() { - mRemotePlayerState = PlayerState.FINISHED; - mDisplayedPlayerState = PlayerState.FINISHED; - updateTitle(null); - } - - /** - * Reset the media route to the default - */ - protected void clearMediaRoute() { - if (getMediaRouter() != null) { - getMediaRouter().getDefaultRoute().select(); - registerRoute(getMediaRouter().getDefaultRoute()); - } - } - - @Override - public boolean currentRouteSupportsRemotePlayback() { - return mCurrentRoute != null && mCurrentRoute.supportsControlCategory( - MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - } - - protected final Context getContext() { - return mContext; - } - - protected final RouteInfo getCurrentRoute() { - return mCurrentRoute; - } - - protected final Handler getHandler() { - return mHandler; - } - - protected final MediaRouter getMediaRouter() { - return mMediaRouter; - } - - @Override - public final MediaStateListener getMediaStateListener() { - return mMediaStateListener; - } - - public final @PlayerState int getRemotePlayerState() { - return mRemotePlayerState; - } - - @Override - public final @PlayerState int getDisplayedPlayerState() { - return mDisplayedPlayerState; - } - - @Override - public final String getRouteName() { - return mCurrentRoute == null ? null : mCurrentRoute.getName(); - } - - protected final Set<UiListener> getUiListeners() { - return mUiListeners; - } - - private final boolean isAtEndOfVideo(long positionMs, long videoLengthMs) { - return videoLengthMs - positionMs < END_OF_VIDEO_THRESHOLD_MS && videoLengthMs > 0; - } - - @Override - public final boolean isBeingCast() { - return (mIsPrepared && mRemotePlayerState != PlayerState.INVALIDATED - && mRemotePlayerState != PlayerState.ERROR - && mRemotePlayerState != PlayerState.FINISHED); - } - - @Override - public final boolean isPlaying() { - return mRemotePlayerState == PlayerState.PLAYING - || mRemotePlayerState == PlayerState.LOADING; - } - - @Override - public final boolean isRemotePlaybackAvailable() { - if (mediaRouterInitializationFailed()) return false; - - return getMediaRouter().getSelectedRoute().getPlaybackType() - == MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE || getMediaRouter().isRouteAvailable( - mMediaRouteSelector, MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); - } - - protected final boolean mediaRouterInitializationFailed() { - return getMediaRouter() == null; - } - - protected final void notifyRouteSelected(RouteInfo route) { - for (UiListener listener : mUiListeners) { - listener.onRouteSelected(route.getName(), this); - } - if (!canCastMedia()) return; - if (mMediaStateListener == null) return; - mMediaStateListener.pauseLocal(); - mMediaStateListener.onCastStarting(route.getName()); - startCastingVideo(); - } - - // This exists for compatibility with old downstream code - // TODO(aberent) convert to abstract - protected void startCastingVideo() { - String url = mMediaStateListener.getSourceUrl(); - Uri uri = url == null ? null : Uri.parse(url); - setDataSource(uri, mMediaStateListener.getCookies()); - prepareAsync( - mMediaStateListener.getFrameUrl(), mMediaStateListener.getStartPositionMillis()); - } - - private boolean canCastMedia() { - return isRemotePlaybackAvailable() && !routeIsDefaultRoute() - && currentRouteSupportsRemotePlayback(); - } - - protected void onRouteAddedEvent(MediaRouter router, RouteInfo route) { - }; - - // TODO(aberent): Merge with onRouteSelected(). Needs two sided patch for downstream - // implementations - protected void onRouteSelectedEvent(MediaRouter router, RouteInfo route) { - } - - @Override - public boolean shouldFilterOutRoute(RouteInfo route) { - return false; - } - - @Override - public void onRouteSelected(MediaStateListener player, MediaRouter router, RouteInfo route) { - if (mMediaStateListener != null) mMediaStateListener.onCastStopping(); - setMediaStateListener(player); - onRouteSelectedEvent(router, route); - } - - protected abstract void onRouteUnselectedEvent(MediaRouter router, RouteInfo route); - - @Override - public void prepareMediaRoute() { - startWatchingRouteSelection(); - } - - @Override - public void release() { - recordEndOfSessionUMA(); - } - - private void recordEndOfSessionUMA() { - long remotePlaybackStoppedTimestampMs = SystemClock.elapsedRealtime(); - - // There was no media element ever... - if (mMediaElementAttachedTimestampMs == 0) return; - - long remotePlaybackIntervalMs = - remotePlaybackStoppedTimestampMs - mMediaElementAttachedTimestampMs; - - if (mMediaElementDetachedTimestampMs == 0) { - mMediaElementDetachedTimestampMs = remotePlaybackStoppedTimestampMs; - } - - int noElementRemotePlaybackTimePercentage = - (int) ((remotePlaybackStoppedTimestampMs - mMediaElementDetachedTimestampMs) * 100 - / remotePlaybackIntervalMs); - RecordCastAction.recordRemoteSessionTimeWithoutMediaElementPercentage( - noElementRemotePlaybackTimePercentage); - mMediaElementAttachedTimestampMs = 0; - mMediaElementDetachedTimestampMs = 0; - } - - protected final void registerRoute(RouteInfo route) { - mCurrentRoute = route; - logRoute("Selected route", route); - } - - @RemovableInRelease - private void logRoute(String message, RouteInfo route) { - if (route != null) { - Log.d(TAG, message + " " + route.getName() + " " + route.getId()); - } - } - - protected void removeAllListeners() { - mUiListeners.clear(); - } - - @Override - public void removeMediaStateListener(MediaStateListener listener) { - if (mediaRouterInitializationFailed()) return; - - mAvailableRouteListeners.remove(listener); - if (mAvailableRouteListeners.isEmpty()) { - getMediaRouter().removeCallback(mDeviceDiscoveryCallback); - Log.d(TAG, "Stopped device discovery"); - } - } - - @Override - public void removeUiListener(UiListener listener) { - mUiListeners.remove(listener); - } - - @Override - public boolean routeIsDefaultRoute() { - return mCurrentRoute != null && mCurrentRoute.isDefault(); - } - - protected void sendErrorToListeners(int error) { - String errorMessage = - getContext().getString(R.string.cast_error_playing_video, mCurrentRoute.getName()); - - for (UiListener listener : mUiListeners) { - listener.onError(error, errorMessage); - } - - if (mMediaStateListener != null) mMediaStateListener.onError(); - } - - @Override - public void setMediaStateListener(MediaStateListener mediaStateListener) { - if (mMediaStateListener != null && mediaStateListener == null - && mMediaElementAttachedTimestampMs != 0) { - mMediaElementDetachedTimestampMs = SystemClock.elapsedRealtime(); - } else if (mMediaStateListener == null && mediaStateListener != null) { - // We're switching the videos so let's record the UMA for the previous one. - if (mMediaElementDetachedTimestampMs != 0) recordEndOfSessionUMA(); - - mMediaElementAttachedTimestampMs = SystemClock.elapsedRealtime(); - mMediaElementDetachedTimestampMs = 0; - } - - mMediaStateListener = mediaStateListener; - } - - private void onCasting() { - if (!mIsPrepared) { - for (UiListener listener : mUiListeners) { - listener.onPrepared(this); - } - if (mMediaStateListener != null) { - if (mMediaStateListener.isPauseRequested()) pause(); - if (mMediaStateListener.isSeekRequested()) { - seekTo(mMediaStateListener.getSeekLocation()); - } else { - seekTo(mMediaStateListener.getLocalPosition()); - } - mMediaStateListener.onCastStarted(); - } - RecordCastAction.castDefaultPlayerResult(true); - mIsPrepared = true; - } - } - - protected void setUnprepared() { - mIsPrepared = false; - } - - protected void showCastError(String routeName) { - Toast toast = Toast.makeText( - getContext(), - getContext().getString(R.string.cast_error_playing_video, routeName), - Toast.LENGTH_SHORT); - toast.show(); - } - - private void startWatchingRouteSelection() { - if (mWatchingRouteSelection || mediaRouterInitializationFailed()) return; - - mWatchingRouteSelection = true; - // Start listening - getMediaRouter().addCallback(mMediaRouteSelector, mDeviceSelectionCallback, - MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); - Log.d(TAG, "Started route selection discovery"); - } - - protected void stopWatchingRouteSelection() { - mWatchingRouteSelection = false; - if (getMediaRouter() != null) { - getMediaRouter().removeCallback(mDeviceSelectionCallback); - Log.d(TAG, "Stopped route selection discovery"); - } - } - - @VisibleForTesting - void setPlayerStateForMediaItemState(int state) { - @PlayerState - int playerState = PlayerState.STOPPED; - switch (state) { - case MediaItemStatus.PLAYBACK_STATE_BUFFERING: - playerState = PlayerState.LOADING; - break; - case MediaItemStatus.PLAYBACK_STATE_CANCELED: - playerState = PlayerState.FINISHED; - break; - case MediaItemStatus.PLAYBACK_STATE_ERROR: - playerState = PlayerState.ERROR; - break; - case MediaItemStatus.PLAYBACK_STATE_FINISHED: - playerState = PlayerState.FINISHED; - break; - case MediaItemStatus.PLAYBACK_STATE_INVALIDATED: - playerState = PlayerState.INVALIDATED; - break; - case MediaItemStatus.PLAYBACK_STATE_PAUSED: - if (isAtEndOfVideo(getPosition(), getDuration())) { - playerState = PlayerState.FINISHED; - } else { - playerState = PlayerState.PAUSED; - } - break; - case MediaItemStatus.PLAYBACK_STATE_PENDING: - playerState = PlayerState.PAUSED; - break; - case MediaItemStatus.PLAYBACK_STATE_PLAYING: - playerState = PlayerState.PLAYING; - break; - default: - break; - } - - mRemotePlayerState = playerState; - } - - protected void updateState(int state) { - Log.d(TAG, "updateState oldState: %s player state: %s", mRemotePlayerState, state); - - @PlayerState - int oldState = mRemotePlayerState; - setPlayerStateForMediaItemState(state); - - Log.d(TAG, "updateState newState: %s", mRemotePlayerState); - - if (oldState != mRemotePlayerState) { - setDisplayedPlayerState(mRemotePlayerState); - switch (mRemotePlayerState) { - case PlayerState.PLAYING: - onCasting(); - break; - case PlayerState.PAUSED: - onCasting(); - break; - case PlayerState.FINISHED: - release(); - break; - case PlayerState.INVALIDATED: - clearItemState(); - break; - case PlayerState.ERROR: - sendErrorToListeners(CastMediaControlIntent.ERROR_CODE_REQUEST_FAILED); - release(); - break; - default: - break; - } - } - } - - protected void setDisplayedPlayerState(@PlayerState int state) { - mDisplayedPlayerState = state; - for (UiListener listener : mUiListeners) { - listener.onPlaybackStateChanged(mDisplayedPlayerState); - } - if (mMediaStateListener != null) { - mMediaStateListener.onPlaybackStateChanged(mDisplayedPlayerState); - } - } - - protected void updateTitle(@Nullable String newTitle) { - for (UiListener listener : mUiListeners) { - listener.onTitleChanged(newTitle); - } - } - - @Override - public Bitmap getPoster() { - if (getMediaStateListener() == null) return null; - return getMediaStateListener().getPosterBitmap(); - } - - // This exists for compatibility with old downstream code - // TODO(aberent) remove - protected void prepareAsync(String frameUrl, long startPositionMillis){}; - - // This exists for compatibility with old downstream code - // TODO(aberent) remove - protected void setDataSource(Uri uri, String cookies){}; - - protected boolean reconnectAnyExistingRoute() { - // Temp version to avoid two sided patch while removing - return false; - }; - - @Override - public void checkIfPlayableRemotely(String sourceUrl, String frameUrl, String cookies, - String userAgent, MediaValidationCallback callback) { - callback.onResult(true, sourceUrl, frameUrl); - } - - @Override - public String getUriPlaying() { - return null; - } - - // Used by J - void setPreparedForTesting() { - mIsPrepared = true; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java deleted file mode 100644 index 106c67c..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java +++ /dev/null
@@ -1,293 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.media.AudioManager; -import android.support.annotation.Nullable; - -import org.chromium.base.ApplicationStatus; -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeTabbedActivity; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; -import org.chromium.chrome.browser.media.ui.MediaNotificationListener; -import org.chromium.chrome.browser.media.ui.MediaNotificationManager; -import org.chromium.chrome.browser.metrics.MediaNotificationUma; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.components.url_formatter.UrlFormatter; -import org.chromium.services.media_session.MediaMetadata; - -import java.net.URI; -import java.net.URISyntaxException; - -/** - * Notification control controls the cast notification and lock screen, using - * {@link MediaNotificationManager} - */ -public class CastNotificationControl implements MediaRouteController.UiListener, - MediaNotificationListener, AudioManager.OnAudioFocusChangeListener { - private static final String TAG = "MediaFling"; - - @SuppressLint("StaticFieldLeak") - private static CastNotificationControl sInstance; - - private Bitmap mPosterBitmap; - protected MediaRouteController mMediaRouteController; - private MediaNotificationInfo.Builder mNotificationBuilder; - private Context mContext; - private @PlayerState int mState; - private String mTitle = ""; - private AudioManager mAudioManager; - - /** - * Contains the origin of the tab containing the video when it's cast. The origin is formatted - * to be presented better from the security POV if the URL is parseable. Otherwise it's just the - * URL of the tab if the tab exists. Can be null. - */ - @Nullable - private String mTabOrigin; - - private boolean mIsShowing; - - private static final Object LOCK = new Object(); - - - private CastNotificationControl(Context context) { - mContext = context; - mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - } - /** - * Get the unique NotificationControl, creating it if necessary. - * @param context The context of the activity - * @param mediaRouteController The current mediaRouteController, if any. - * @return a {@code LockScreenTransportControl} based on the platform's SDK API or null if the - * current platform's SDK API is not supported. - */ - public static CastNotificationControl getOrCreate(Context context, - @Nullable MediaRouteController mediaRouteController) { - synchronized (LOCK) { - if (sInstance == null) { - sInstance = new CastNotificationControl(context); - } - sInstance.setRouteController(mediaRouteController); - return sInstance; - } - } - - /** - * @return the poster bitmap previously assigned with {@link #setPosterBitmap(Bitmap)}, or - * {@code null} if the poster bitmap has not yet been assigned. - */ - public final Bitmap getPosterBitmap() { - return mPosterBitmap; - } - - /** - * Sets the poster bitmap to display on the TransportControl. - */ - public final void setPosterBitmap(Bitmap posterBitmap) { - if (mPosterBitmap == posterBitmap - || (mPosterBitmap != null && mPosterBitmap.sameAs(posterBitmap))) { - return; - } - mPosterBitmap = posterBitmap; - if (mNotificationBuilder == null || mMediaRouteController == null) return; - - updateNotificationBuilderIfPosterIsGoodEnough(); - mNotificationBuilder.setNotificationLargeIcon(mMediaRouteController.getPoster()); - updateNotification(); - } - - public void hide() { - mIsShowing = false; - MediaNotificationManager.hide(Tab.INVALID_TAB_ID, R.id.remote_notification); - mAudioManager.abandonAudioFocus(this); - mMediaRouteController.removeUiListener(this); - } - - public void show(@PlayerState int initialState) { - mMediaRouteController.addUiListener(this); - // TODO(aberent): investigate why this is necessary, and whether we are handling - // it correctly. Also add code to restore it when Chrome is resumed. - mAudioManager.requestAudioFocus(this, AudioManager.USE_DEFAULT_STREAM_TYPE, - AudioManager.AUDIOFOCUS_GAIN); - Intent contentIntent = new Intent(mContext, ExpandedControllerActivity.class); - contentIntent.putExtra( - MediaNotificationUma.INTENT_EXTRA_NAME, MediaNotificationUma.Source.MEDIA_FLING); - mNotificationBuilder = new MediaNotificationInfo.Builder() - .setPaused(false) - .setPrivate(false) - .setNotificationSmallIcon(R.drawable.ic_notification_media_route) - .setContentIntent(contentIntent) - .setDefaultNotificationLargeIcon(R.drawable.cast_playing_square) - .setId(R.id.remote_notification) - .setListener(this); - - updateNotificationBuilderIfPosterIsGoodEnough(); - mState = initialState; - - updateNotification(); - mIsShowing = true; - } - - public void setRouteController(MediaRouteController controller) { - if (mMediaRouteController != null) mMediaRouteController.removeUiListener(this); - mMediaRouteController = controller; - if (controller != null) controller.addUiListener(this); - } - - private void updateNotification() { - // Nothing shown yet, nothing to update. - if (mNotificationBuilder == null) return; - - mNotificationBuilder.setMetadata(new MediaMetadata(mTitle, "", "")); - if (mTabOrigin != null) mNotificationBuilder.setOrigin(mTabOrigin); - - if (mState == PlayerState.PAUSED || mState == PlayerState.PLAYING) { - mNotificationBuilder.setPaused(mState != PlayerState.PLAYING); - mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP - | MediaNotificationInfo.ACTION_PLAY_PAUSE); - MediaNotificationManager.show(mNotificationBuilder.build()); - } else if (mState == PlayerState.LOADING) { - mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP); - MediaNotificationManager.show(mNotificationBuilder.build()); - } else { - hide(); - } - } - - // TODO(aberent) at the moment this is only called from a test, but it should be called if the - // poster changes. - public void onPosterBitmapChanged() { - if (mNotificationBuilder == null || mMediaRouteController == null) return; - updateNotificationBuilderIfPosterIsGoodEnough(); - updateNotification(); - } - - // MediaRouteController.UiListener implementation. - @Override - public void onPlaybackStateChanged(@PlayerState int newState) { - if (!mIsShowing - && (newState == PlayerState.PLAYING || newState == PlayerState.LOADING - || newState == PlayerState.PAUSED)) { - show(newState); - return; - } - - if (mState == newState - || mState == PlayerState.PAUSED && newState == PlayerState.LOADING && mIsShowing) { - return; - } - - mState = newState; - updateNotification(); - } - - @Override - public void onRouteSelected(String name, MediaRouteController mediaRouteController) { - // The notification will be shown/updated later so don't update it in case it's still - // showing for the previous video. - mTabOrigin = getCurrentTabOrigin(); - } - - @Override - public void onRouteUnselected(MediaRouteController mediaRouteController) { - hide(); - } - - @Override - public void onPrepared(MediaRouteController mediaRouteController) { - } - - @Override - public void onError(int errorType, String message) { - } - - @Override - public void onDurationUpdated(long durationMillis) { - } - - @Override - public void onPositionChanged(long positionMillis) { - } - - @Override - public void onTitleChanged(String title) { - if (title == null || title.equals(mTitle)) return; - - mTitle = title; - updateNotification(); - } - - // MediaNotificationListener methods - @Override - public void onPlay(int actionSource) { - mMediaRouteController.resume(); - } - - @Override - public void onPause(int actionSource) { - mMediaRouteController.pause(); - } - - @Override - public void onStop(int actionSource) { - mMediaRouteController.release(); - } - - @Override - public void onMediaSessionAction(int action) {} - - // AudioManager.OnAudioFocusChangeListener methods - @Override - public void onAudioFocusChange(int focusChange) { - // Do nothing. The remote player should keep playing. - } - - @VisibleForTesting - static CastNotificationControl getForTests() { - return sInstance; - } - - @VisibleForTesting - boolean isShowingForTests() { - return mIsShowing; - } - - private void updateNotificationBuilderIfPosterIsGoodEnough() { - Bitmap poster = mMediaRouteController.getPoster(); - if (MediaNotificationManager.isBitmapSuitableAsMediaImage(poster)) { - mNotificationBuilder.setNotificationLargeIcon(poster); - mNotificationBuilder.setMediaSessionImage(poster); - } - } - - private String getCurrentTabOrigin() { - Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); - - if (!(activity instanceof ChromeTabbedActivity)) return null; - - Tab tab = ((ChromeTabbedActivity) activity).getActivityTab(); - if (tab == null || !tab.isInitialized()) return null; - - String url = tab.getUrl(); - try { - URI uri = new URI(url); - return UrlFormatter.formatUrlForSecurityDisplay(url); - } catch (URISyntaxException | UnsatisfiedLinkError e) { - // UnstatisfiedLinkError can only happen in tests as the natives are not initialized - // yet. - Log.e(TAG, "Unable to parse the origin from the URL. Using the full URL instead."); - return url; - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/DefaultMediaRouteController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/DefaultMediaRouteController.java deleted file mode 100644 index dac7692a..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/DefaultMediaRouteController.java +++ /dev/null
@@ -1,866 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.media.remote; - -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.Uri; -import android.os.Bundle; -import android.os.SystemClock; -import android.support.annotation.Nullable; -import android.support.v7.media.MediaControlIntent; -import android.support.v7.media.MediaItemMetadata; -import android.support.v7.media.MediaItemStatus; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; -import android.support.v7.media.MediaSessionStatus; - -import com.google.android.gms.cast.CastMediaControlIntent; - -import org.chromium.base.ApplicationState; -import org.chromium.base.ApplicationStatus; -import org.chromium.base.Log; -import org.chromium.base.annotations.RemovableInRelease; -import org.chromium.base.annotations.UsedByReflection; -import org.chromium.base.task.AsyncTask; -import org.chromium.chrome.browser.UrlConstants; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; - -import java.net.URI; -import java.net.URISyntaxException; - -/** - * Class that abstracts all communication to and from the Android MediaRoutes. This class is - * responsible for connecting to the MRs as well as sending commands and receiving status updates - * from the remote player. - * - * We have two main scenarios for Cast: - * - * - the first cast: user plays the first video on the Chromecast so we start a new session with - * the player and fling the video - * - * - the consequent cast: users plays another video while the previous one is still playing - * remotely meaning that we don't have to start the session but to replace the current video with - * the new one - * - * Casting the first video takes two intents sent to the selected media route: - * ACTION_START_SESSION and ACTION_PLAY. The first one is sent before anything else. We get the - * session id from the result bundle of the intent but need to wait until the session becomes - * active before sending the video URL via the ACTION_PLAY intent. - * - * Casting the second video to the same target device only takes one ACTION_PLAY intent if - * the session is still active. Otherwise, the scenario is the same as for the first video. - */ -@UsedByReflection("RemoteMediaPlayerController.java") -public class DefaultMediaRouteController extends AbstractMediaRouteController { - - /** - * Interface for MediaRouter intents result handlers. - */ - protected interface ResultBundleHandler { - void onResult(Bundle data); - - void onError(String message, Bundle data); - } - - private static final String TAG = "MediaFling"; - - private static final String ACTION_RECEIVE_SESSION_STATUS_UPDATE = - "com.google.android.apps.chrome.videofling.RECEIVE_SESSION_STATUS_UPDATE"; - private static final String ACTION_RECEIVE_MEDIA_STATUS_UPDATE = - "com.google.android.apps.chrome.videofling.RECEIVE_MEDIA_STATUS_UPDATE"; - private static final String MIME_TYPE = "video/mp4"; - private String mCurrentSessionId; - private String mCurrentItemId; - private boolean mSeeking; - private final String mIntentCategory; - private PendingIntent mSessionStatusUpdateIntent; - private BroadcastReceiver mSessionStatusBroadcastReceiver; - private PendingIntent mMediaStatusUpdateIntent; - private BroadcastReceiver mMediaStatusBroadcastReceiver; - - private String mPreferredTitle; - private long mStartPositionMillis; - private final PositionExtrapolator mPositionExtrapolator = new PositionExtrapolator(); - - private Uri mLocalVideoUri; - - private int mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; - - private final ApplicationStatus.ApplicationStateListener mApplicationStateListener = - new ApplicationStatus.ApplicationStateListener() { - @Override - public void onApplicationStateChange(int newState) { - // HAS_DESTROYED_ACTIVITIES means all Chrome activities have been destroyed. - if (newState == ApplicationState.HAS_DESTROYED_ACTIVITIES) { - onActivitiesDestroyed(); - } - } - }; - - /** - * Default and only constructor. - */ - public DefaultMediaRouteController() { - mIntentCategory = getContext().getPackageName(); - } - - @Override - public boolean initialize() { - if (mediaRouterInitializationFailed()) return false; - - ApplicationStatus.registerApplicationStateListener(mApplicationStateListener); - - if (mSessionStatusUpdateIntent == null) { - Intent sessionStatusUpdateIntent = new Intent(ACTION_RECEIVE_SESSION_STATUS_UPDATE); - sessionStatusUpdateIntent.addCategory(mIntentCategory); - mSessionStatusUpdateIntent = PendingIntent.getBroadcast(getContext(), 0, - sessionStatusUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT); - } - - if (mMediaStatusUpdateIntent == null) { - Intent mediaStatusUpdateIntent = new Intent(ACTION_RECEIVE_MEDIA_STATUS_UPDATE); - mediaStatusUpdateIntent.addCategory(mIntentCategory); - mMediaStatusUpdateIntent = PendingIntent.getBroadcast(getContext(), 0, - mediaStatusUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT); - } - - return true; - } - - @Override - public boolean canPlayMedia(String sourceUrl, String frameUrl) { - if (mediaRouterInitializationFailed() || sourceUrl == null) return false; - - try { - String scheme = new URI(sourceUrl).getScheme(); - if (scheme == null) return false; - return scheme.equals(UrlConstants.HTTP_SCHEME) - || scheme.equals(UrlConstants.HTTPS_SCHEME); - } catch (URISyntaxException e) { - return false; - } - } - - @Override - public void setRemoteVolume(int delta) { - boolean canChangeRemoteVolume = (getCurrentRoute().getVolumeHandling() - == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE); - if (currentRouteSupportsRemotePlayback() && canChangeRemoteVolume) { - getCurrentRoute().requestUpdateVolume(delta); - } - } - - @Override - public MediaRouteSelector buildMediaRouteSelector() { - return new MediaRouteSelector.Builder().addControlCategory( - CastMediaControlIntent.categoryForRemotePlayback(getCastReceiverId())).build(); - } - - protected String getCastReceiverId() { - return CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID; - } - - @Override - public void resume() { - if (mCurrentItemId == null) return; - - Intent intent = new Intent(MediaControlIntent.ACTION_RESUME); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - sendIntentToRoute(intent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - processMediaStatusBundle(data); - } - - @Override - public void onError(String message, Bundle data) { - release(); - } - }); - - setDisplayedPlayerState(PlayerState.LOADING); - } - - @Override - public void pause() { - if (mCurrentItemId == null) return; - - Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - sendIntentToRoute(intent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - processMediaStatusBundle(data); - } - - @Override - public void onError(String message, Bundle data) { - // Do not release the player just because of a failed pause - // request. This can happen when pausing more than once for - // example. - } - }); - - // Update the last known position to the current one so that we don't - // jump back in time discarding whatever we extrapolated from the last - // time the position was updated. - mPositionExtrapolator.onPaused(); - setDisplayedPlayerState(PlayerState.PAUSED); - } - - /** - * Plays the given Uri on the currently selected player. This will replace any currently playing - * video - * - * @param preferredTitle the preferred title of the current playback session to display - * @param startPositionMillis from which to start playing. - */ - private void playUri(@Nullable final String preferredTitle, final long startPositionMillis) { - RecordCastAction.castMediaType(MediaUrlResolver.getMediaType(mLocalVideoUri)); - installBroadcastReceivers(); - - // If the session is already started (meaning we are casting a video already), we simply - // load the new URL with one ACTION_PLAY intent. - if (mCurrentSessionId != null) { - Log.d(TAG, "Playing a new url: %s", mLocalVideoUri); - - // We keep the same session so only clear the playing item status. - clearItemState(); - startPlayback(preferredTitle, startPositionMillis); - return; - } - - Log.d(TAG, "Sending stream to app: %s", getCastReceiverId()); - Log.d(TAG, "Url: %s", mLocalVideoUri); - - startSession(true, null, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - configureNewSession(data); - - mPreferredTitle = preferredTitle; - updateTitle(mPreferredTitle); - mStartPositionMillis = startPositionMillis; - // Make sure we get a session status. If the session becomes active - // immediately then the broadcast session status can arrive before we have - // the session id, so this ensures we get it whatever happens. - getSessionStatus(mCurrentSessionId); - } - - @Override - public void onError(String message, Bundle data) { - release(); - RecordCastAction.castDefaultPlayerResult(false); - } - }); - } - - /** - * Send a start session intent. - * - * @param relaunch Whether we should relaunch the cast application. - * @param resultBundleHandler BundleHandler to handle reply. - */ - private void startSession(boolean relaunch, String sessionId, - ResultBundleHandler resultBundleHandler) { - Intent intent = new Intent(MediaControlIntent.ACTION_START_SESSION); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - - intent.putExtra(CastMediaControlIntent.EXTRA_CAST_STOP_APPLICATION_WHEN_SESSION_ENDS, true); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER, - mSessionStatusUpdateIntent); - intent.putExtra(CastMediaControlIntent.EXTRA_CAST_APPLICATION_ID, getCastReceiverId()); - intent.putExtra(CastMediaControlIntent.EXTRA_CAST_RELAUNCH_APPLICATION, relaunch); - if (sessionId != null) intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId); - - addIntentExtraForDebugLogging(intent); - sendIntentToRoute(intent, resultBundleHandler); - } - - @RemovableInRelease - private void addIntentExtraForDebugLogging(Intent intent) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - intent.putExtra(CastMediaControlIntent.EXTRA_DEBUG_LOGGING_ENABLED, true); - } - } - - private void getSessionStatus(String sessionId) { - Intent intent = new Intent(MediaControlIntent.ACTION_GET_SESSION_STATUS); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId); - - sendIntentToRoute(intent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - logBundle("getSessionStatus result :", data); - processSessionStatusBundle(data); - } - - @Override - public void onError(String message, Bundle data) { - release(); - } - }); - } - - private void startPlayback( - @Nullable final String preferredTitle, final long startPositionMillis) { - setUnprepared(); - Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - intent.setDataAndType(mLocalVideoUri, MIME_TYPE); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER, - mMediaStatusUpdateIntent); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, startPositionMillis); - - if (preferredTitle != null) { - Bundle metadata = new Bundle(); - metadata.putString(MediaItemMetadata.KEY_TITLE, preferredTitle); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata); - } - - sendIntentToRoute(intent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - mCurrentItemId = data.getString(MediaControlIntent.EXTRA_ITEM_ID); - processMediaStatusBundle(data); - RecordCastAction.castDefaultPlayerResult(true); - } - - @Override - public void onError(String message, Bundle data) { - release(); - RecordCastAction.castDefaultPlayerResult(false); - } - }); - } - - @Override - public long getPosition() { - return mPositionExtrapolator.getPosition(); - } - - @Override - public long getDuration() { - return mPositionExtrapolator.getDuration(); - } - - @Override - public void seekTo(long msec) { - if (msec == getPosition()) return; - // Update the position now since the MRP will update it only once the video is playing - // remotely. In particular, if the video is paused, the MRP doesn't send the command until - // the video is resumed. - mPositionExtrapolator.onSeek(msec); - mSeeking = true; - Intent intent = new Intent(MediaControlIntent.ACTION_SEEK); - intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, mCurrentItemId); - intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, msec); - sendIntentToRoute(intent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - if (getMediaStateListener() != null) getMediaStateListener().onSeekCompleted(); - processMediaStatusBundle(data); - } - - @Override - public void onError(String message, Bundle data) { - release(); - } - }); - } - - @Override - public void release() { - super.release(); - - for (UiListener listener : getUiListeners()) { - listener.onRouteUnselected(this); - } - if (getMediaStateListener() != null) getMediaStateListener().onRouteUnselected(); - setMediaStateListener(null); - - if (mediaRouterInitializationFailed()) return; - if (mCurrentSessionId == null) { - // This can happen if we disconnect after a failure (because the - // media could not be casted). - disconnect(); - return; - } - - Intent stopIntent = new Intent(MediaControlIntent.ACTION_STOP); - stopIntent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - stopIntent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - - sendIntentToRoute(stopIntent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - processMediaStatusBundle(data); - } - - @Override - public void onError(String message, Bundle data) {} - }); - - Intent endSessionIntent = new Intent(MediaControlIntent.ACTION_END_SESSION); - endSessionIntent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); - endSessionIntent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mCurrentSessionId); - - sendIntentToRoute(endSessionIntent, new ResultBundleHandler() { - @Override - public void onResult(Bundle data) { - logMediaSessionStatus(data); - - for (UiListener listener : getUiListeners()) { - listener.onPlaybackStateChanged(PlayerState.FINISHED); - } - - if (getMediaStateListener() != null) { - getMediaStateListener().onPlaybackStateChanged(PlayerState.FINISHED); - } - recordRemainingTimeUMA(); - disconnect(); - } - - @Override - public void onError(String message, Bundle data) { - disconnect(); - } - }); - } - - /** - * Disconnect from the remote screen without stopping the media playing. use release() for - * disconnect + stop. - */ - private void disconnect() { - clearStreamState(); - clearMediaRoute(); - - if (mSessionStatusBroadcastReceiver != null) { - getContext().unregisterReceiver(mSessionStatusBroadcastReceiver); - mSessionStatusBroadcastReceiver = null; - } - if (mMediaStatusBroadcastReceiver != null) { - getContext().unregisterReceiver(mMediaStatusBroadcastReceiver); - mMediaStatusBroadcastReceiver = null; - } - clearConnectionFailureCallback(); - - stopWatchingRouteSelection(); - removeAllListeners(); - } - - @Override - protected void onRouteSelectedEvent(MediaRouter router, RouteInfo route) { - Log.d(TAG, "Selected route %s", route); - if (!route.isSelected()) return; - - RecordCastAction.castPlayRequested(); - - RecordCastAction.remotePlaybackDeviceSelected(RecordCastAction.DeviceType.CAST_GENERIC); - installBroadcastReceivers(); - - if (getMediaStateListener() == null) { - showCastError(route.getName()); - release(); - return; - } - - if (route != getCurrentRoute()) { - registerRoute(route); - clearStreamState(); - } - mPositionExtrapolator.clear(); - - notifyRouteSelected(route); - } - - /* - * Although our custom implementation of the disconnect button doesn't need this, it is needed - * when the route is released due to, for example, another application stealing the route, or - * when we switch to a YouTube video on the same device. - */ - @Override - protected void onRouteUnselectedEvent(MediaRouter router, RouteInfo route) { - Log.d(TAG, "Unselected route %s", route); - // Preserve our best guess as to the final position; this is needed to reset the - // local position while switching back to local playback. - mPositionExtrapolator.onPaused(); - if (getCurrentRoute() != null && route.getId().equals(getCurrentRoute().getId())) { - clearStreamState(); - } - } - - private void installBroadcastReceivers() { - if (mSessionStatusBroadcastReceiver == null) { - mSessionStatusBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - logIntent("Got a session broadcast intent from the MRP: ", intent); - Bundle statusBundle = intent.getExtras(); - - // Ignore null status bundles. - if (statusBundle == null) return; - - // Ignore the status of old sessions. - String sessionId = statusBundle.getString(MediaControlIntent.EXTRA_SESSION_ID); - if (mCurrentSessionId == null || !mCurrentSessionId.equals(sessionId)) return; - - processSessionStatusBundle(statusBundle); - } - }; - IntentFilter sessionBroadcastIntentFilter = - new IntentFilter(ACTION_RECEIVE_SESSION_STATUS_UPDATE); - sessionBroadcastIntentFilter.addCategory(mIntentCategory); - getContext().registerReceiver(mSessionStatusBroadcastReceiver, - sessionBroadcastIntentFilter); - } - - if (mMediaStatusBroadcastReceiver == null) { - mMediaStatusBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - logIntent("Got a broadcast intent from the MRP: ", intent); - - processMediaStatusBundle(intent.getExtras()); - } - }; - IntentFilter mediaBroadcastIntentFilter = - new IntentFilter(ACTION_RECEIVE_MEDIA_STATUS_UPDATE); - mediaBroadcastIntentFilter.addCategory(mIntentCategory); - getContext().registerReceiver(mMediaStatusBroadcastReceiver, - mediaBroadcastIntentFilter); - } - } - - /** - * Called when the main activity receives an onDestroy() call. - */ - protected void onActivitiesDestroyed() { - ApplicationStatus.unregisterApplicationStateListener(mApplicationStateListener); - release(); - } - - /** - * Clear the session and the currently playing item (if any). - */ - protected void clearStreamState() { - mLocalVideoUri = null; - mCurrentSessionId = null; - clearItemState(); - } - - @Override - protected void clearItemState() { - // Note: do not clear the stream position, since this is still needed so - // that we can reset the local stream position to match. - super.clearItemState(); - mCurrentItemId = null; - mPositionExtrapolator.clear(); - mSeeking = false; - } - - private void processSessionStatusBundle(Bundle statusBundle) { - MediaSessionStatus status = MediaSessionStatus.fromBundle( - statusBundle.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS)); - int sessionState = status.getSessionState(); - - // If no change do nothing - if (sessionState == mSessionState) return; - mSessionState = sessionState; - - switch (sessionState) { - case MediaSessionStatus.SESSION_STATE_ACTIVE: - if (mLocalVideoUri != null) { - startPlayback(mPreferredTitle, mStartPositionMillis); - } - break; - - case MediaSessionStatus.SESSION_STATE_ENDED: - case MediaSessionStatus.SESSION_STATE_INVALIDATED: - for (UiListener listener : getUiListeners()) { - listener.onPlaybackStateChanged(PlayerState.INVALIDATED); - } - if (getMediaStateListener() != null) { - getMediaStateListener().onPlaybackStateChanged(PlayerState.INVALIDATED); - } - // Record the remaining time UMA first, otherwise the playback state will be cleared - // in release(). - recordRemainingTimeUMA(); - // Set the current session id to null so we don't send the stop intent. - mCurrentSessionId = null; - release(); - break; - - default: - break; - } - } - - private void processMediaStatusBundle(Bundle statusBundle) { - if (statusBundle == null) return; - logBundle("processMediaStatusBundle: ", statusBundle); - - String itemId = statusBundle.getString(MediaControlIntent.EXTRA_ITEM_ID); - if (itemId == null || !itemId.equals(mCurrentItemId)) return; - - // Extract item metadata, if available. - if (statusBundle.containsKey(MediaControlIntent.EXTRA_ITEM_METADATA)) { - Bundle metadataBundle = - (Bundle) statusBundle.getParcelable(MediaControlIntent.EXTRA_ITEM_METADATA); - updateTitle(metadataBundle.getString(MediaItemMetadata.KEY_TITLE, mPreferredTitle)); - } - - // Extract the item status, if available. - if (statusBundle.containsKey(MediaControlIntent.EXTRA_ITEM_STATUS)) { - Bundle itemStatusBundle = - (Bundle) statusBundle.getParcelable(MediaControlIntent.EXTRA_ITEM_STATUS); - MediaItemStatus itemStatus = MediaItemStatus.fromBundle(itemStatusBundle); - - logBundle("Received item status: ", itemStatusBundle); - - updateState(itemStatus.getPlaybackState()); - - // Update the PositionExtrapolator that the playback state has changed. - if (itemStatus.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) { - mPositionExtrapolator.onResumed(); - } else if (itemStatus.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED) { - mPositionExtrapolator.onFinished(); - } else { - mPositionExtrapolator.onPaused(); - } - - if ((getRemotePlayerState() == PlayerState.PAUSED) - || (getRemotePlayerState() == PlayerState.PLAYING) - || (getRemotePlayerState() == PlayerState.LOADING)) { - this.mCurrentItemId = itemId; - - // duration can possibly be -1 if it's unknown, so cap to 0 - long duration = Math.max(itemStatus.getContentDuration(), 0); - // update the position using the remote player's position - // duration can possibly be -1 if it's unknown, so cap to 0 - long position = Math.min(Math.max(itemStatus.getContentPosition(), 0), duration); - // TODO(zqzhang): The GMS core currently uses SystemClock.uptimeMillis() as - // timestamp, which does not conform to the MediaRouter support library docs. See - // b/28378525 and - // http://developer.android.com/reference/android/support/v7/media/MediaItemStatus.html#getTimestamp(). - // Override the timestamp with elapsedRealtime() by assuming the delay between the - // GMS core produces the MediaItemStatus and the code reaches here is short enough. - // long timestamp = itemStatus.getTimestamp(); - long timestamp = SystemClock.elapsedRealtime(); - notifyDurationUpdated(duration); - notifyPositionUpdated(position); - mPositionExtrapolator.onPositionInfoUpdated(duration, position, timestamp); - - if (mSeeking) { - mSeeking = false; - if (getMediaStateListener() != null) getMediaStateListener().onSeekCompleted(); - } - } - logExtraHttpInfo(itemStatus.getExtras()); - } - } - - /** - * Send the given intent to the current route. The result will be returned in the given - * ResultBundleHandler. This function will also check to see if the current route can handle the - * intent before sending it. - * - * @param intent the intent to send to the current route. - * @param bundleHandler contains the result of sending the intent - */ - private void sendIntentToRoute(final Intent intent, final ResultBundleHandler bundleHandler) { - if (getCurrentRoute() == null) { - logIntent("sendIntentToRoute ", intent); - Log.d(TAG, "The current route is null."); - if (bundleHandler != null) bundleHandler.onError(null, null); - return; - } - - if (!getCurrentRoute().supportsControlRequest(intent)) { - logIntent("sendIntentToRoute ", intent); - Log.d(TAG, "The intent is not supported by the route: %s", getCurrentRoute()); - if (bundleHandler != null) bundleHandler.onError(null, null); - return; - } - - sendControlIntent(intent, bundleHandler); - } - - private void sendControlIntent(final Intent intent, final ResultBundleHandler bundleHandler) { - Log.d(TAG, "Sending intent to %s %s", getCurrentRoute().getName(), - getCurrentRoute().getId()); - logIntent("sendControlIntent ", intent); - if (getCurrentRoute().isDefault()) { - Log.d(TAG, "Route is default, not sending"); - return; - } - - getCurrentRoute().sendControlRequest(intent, new MediaRouter.ControlRequestCallback() { - @Override - public void onResult(Bundle data) { - if (data != null && bundleHandler != null) bundleHandler.onResult(data); - } - - @Override - public void onError(String message, Bundle data) { - logControlRequestError(intent, message, data); - int errorCode = 0; - if (data != null) { - errorCode = data.getInt(CastMediaControlIntent.EXTRA_ERROR_CODE); - } - - sendErrorToListeners(errorCode); - - if (bundleHandler != null) bundleHandler.onError(message, data); - } - }); - } - - private void notifyDurationUpdated(long durationMillis) { - for (UiListener listener : getUiListeners()) { - listener.onDurationUpdated(durationMillis); - } - } - - private void notifyPositionUpdated(long position) { - for (UiListener listener : getUiListeners()) { - listener.onPositionChanged(position); - } - } - - private void recordRemainingTimeUMA() { - long duration = getDuration(); - long remainingTime = Math.max(0, duration - getPosition()); - // Duration has already been cleared. - if (getDuration() <= 0) return; - RecordCastAction.castEndedTimeRemaining(duration, remainingTime); - } - - private String bundleToString(Bundle bundle) { - if (bundle == null) return ""; - - StringBuilder extras = new StringBuilder(); - extras.append("["); - for (String key : bundle.keySet()) { - Object value = bundle.get(key); - String valueText = value == null ? "null" : value.toString(); - if (value instanceof Bundle) valueText = bundleToString((Bundle) value); - extras.append(key).append("=").append(valueText).append(","); - } - extras.append("]"); - return extras.toString(); - } - - @Override - protected void startCastingVideo() { - MediaStateListener listener = getMediaStateListener(); - if (listener == null) return; - - String url = listener.getSourceUrl(); - - Log.d(TAG, "startCastingVideo called, url = %s", url); - - // checkIfPlayableRemotely will have rejected null URLs. - assert url != null; - - RecordCastAction.castDomainAndRegistry(listener.getFrameUrl().toString()); - - mLocalVideoUri = Uri.parse(url); - mStartPositionMillis = listener.getStartPositionMillis(); - playUri(listener.getTitle(), mStartPositionMillis); - } - - private void configureNewSession(Bundle data) { - mCurrentSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID); - mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; - Log.d(TAG, "Got a session id: %s", mCurrentSessionId); - } - - @Override - public void checkIfPlayableRemotely(final String sourceUrl, final String frameUrl, - final String cookies, String userAgent, final MediaValidationCallback callback) { - new MediaUrlResolver(new MediaUrlResolver.Delegate() { - - @Override - public Uri getUri() { - return Uri.parse(sourceUrl); - } - - @Override - public String getCookies() { - return cookies; - } - - @Override - public void deliverResult(Uri uri, boolean playable) { - callback.onResult(playable, uri.toString(), frameUrl); - } - - // Some webpages have >100 media files, which causes the THREAD_POOL_EXECUTOR to get - // flooded with requests and causes a crash. We use SERIAL_EXECUTOR instead, to allow - // for an unlimited number of tasks. See https://crbug.com/873941. - }, userAgent).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } - - @Override - public String getUriPlaying() { - if (mLocalVideoUri == null) return null; - return mLocalVideoUri.toString(); - } - - @RemovableInRelease - private void logBundle(String message, Bundle bundle) { - Log.d(TAG, message + bundleToString(bundle)); - } - - @RemovableInRelease - private void logControlRequestError(Intent intent, String message, Bundle data) { - // The intent may contain some PII so we don't want to log it in the released - // version by default. - Log.e(TAG, String.format( - "Error sending control request %s %s. Data: %s Error: %s", intent, - bundleToString(intent.getExtras()), bundleToString(data), message)); - } - - @RemovableInRelease - private void logExtraHttpInfo(Bundle extras) { - if (extras != null) { - if (extras.containsKey(MediaItemStatus.EXTRA_HTTP_STATUS_CODE)) { - int httpStatus = extras.getInt(MediaItemStatus.EXTRA_HTTP_STATUS_CODE); - Log.d(TAG, "HTTP status: %s", httpStatus); - } - if (extras.containsKey(MediaItemStatus.EXTRA_HTTP_RESPONSE_HEADERS)) { - Bundle headers = extras.getBundle(MediaItemStatus.EXTRA_HTTP_RESPONSE_HEADERS); - Log.d(TAG, "HTTP headers: %s", bundleToString(headers)); - } - } - } - - @RemovableInRelease - private void logIntent(String prefix, Intent intent) { - Log.d(TAG, prefix + intent + " extras: " + bundleToString(intent.getExtras())); - } - - @RemovableInRelease - private void logMediaSessionStatus(Bundle data) { - MediaSessionStatus status = MediaSessionStatus.fromBundle( - data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS)); - int sessionState = status.getSessionState(); - Log.d(TAG, "Session state after ending session: %s", sessionState); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java deleted file mode 100644 index 03398725..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java +++ /dev/null
@@ -1,364 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.FragmentActivity; -import android.support.v4.media.session.PlaybackStateCompat; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.TextView; - -import com.google.android.gms.cast.CastMediaControlIntent; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.chrome.browser.metrics.MediaNotificationUma; -import org.chromium.third_party.android.media.MediaController; - -/** - * The activity that's opened by clicking the video flinging (casting) notification. - * - * TODO(aberent): Refactor to merge some common logic with {@link CastNotificationControl}. - */ -public class ExpandedControllerActivity - extends FragmentActivity implements MediaRouteController.UiListener { - private static final int PROGRESS_UPDATE_PERIOD_IN_MS = 1000; - // The alpha value for the poster/placeholder image, an integer between 0 and 256 (opaque). - private static final int POSTER_IMAGE_ALPHA = 200; - - private Handler mHandler; - // We don't use the standard android.media.MediaController, but a custom one. - // See the class itself for details. - private MediaController mMediaController; - private FullscreenMediaRouteButton mMediaRouteButton; - private MediaRouteController mMediaRouteController; - private RemoteVideoInfo mVideoInfo; - private String mScreenName; - - /** - * Handle actions from on-screen media controls. - */ - private MediaController.Delegate mControllerDelegate = new MediaController.Delegate() { - @Override - public void play() { - if (mMediaRouteController == null) return; - mMediaRouteController.resume(); - RecordCastAction.recordFullscreenControlsAction( - RecordCastAction.FullScreenControls.RESUME, - mMediaRouteController.getMediaStateListener() != null); - } - - @Override - public void pause() { - if (mMediaRouteController == null) return; - mMediaRouteController.pause(); - RecordCastAction.recordFullscreenControlsAction( - RecordCastAction.FullScreenControls.PAUSE, - mMediaRouteController.getMediaStateListener() != null); - } - - @Override - public long getDuration() { - if (mMediaRouteController == null) return 0; - return mMediaRouteController.getDuration(); - } - - @Override - public long getPosition() { - if (mMediaRouteController == null) return 0; - return mMediaRouteController.getPosition(); - } - - @Override - public void seekTo(long pos) { - if (mMediaRouteController == null) return; - mMediaRouteController.seekTo(pos); - RecordCastAction.recordFullscreenControlsAction( - RecordCastAction.FullScreenControls.SEEK, - mMediaRouteController.getMediaStateListener() != null); - } - - @Override - public boolean isPlaying() { - if (mMediaRouteController == null) return false; - return mMediaRouteController.isPlaying(); - } - - @Override - public long getActionFlags() { - long flags = - PlaybackStateCompat.ACTION_REWIND | PlaybackStateCompat.ACTION_FAST_FORWARD; - if (mMediaRouteController != null && mMediaRouteController.isPlaying()) { - flags |= PlaybackStateCompat.ACTION_PAUSE; - } else { - flags |= PlaybackStateCompat.ACTION_PLAY; - } - return flags; - } - }; - - private Runnable mProgressUpdater = new Runnable() { - @Override - public void run() { - if (mMediaRouteController.isPlaying()) { - mMediaController.updateProgress(); - mHandler.postDelayed(this, PROGRESS_UPDATE_PERIOD_IN_MS); - } else { - mHandler.removeCallbacks(this); - } - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - MediaNotificationUma.recordClickSource(getIntent()); - - mMediaRouteController = - RemoteMediaPlayerController.instance().getCurrentlyPlayingMediaRouteController(); - - if (mMediaRouteController == null || mMediaRouteController.routeIsDefaultRoute()) { - // We don't want to do anything for the default (local) route - finish(); - return; - } - - // Make the activity full screen. - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - - // requestWindowFeature must be called before adding content. - setContentView(R.layout.expanded_cast_controller); - mHandler = new Handler(); - - ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content); - rootView.setBackgroundColor(Color.BLACK); - - mMediaRouteController.addUiListener(this); - - // Create and initialize the media control UI. - mMediaController = (MediaController) findViewById(R.id.cast_media_controller); - mMediaController.setDelegate(mControllerDelegate); - - View button = getLayoutInflater().inflate(R.layout.cast_controller_media_route_button, - rootView, false); - - if (button instanceof FullscreenMediaRouteButton) { - mMediaRouteButton = (FullscreenMediaRouteButton) button; - rootView.addView(mMediaRouteButton); - mMediaRouteButton.bringToFront(); - mMediaRouteButton.initialize(mMediaRouteController); - } else { - mMediaRouteButton = null; - } - - // Initialize the video info. - setVideoInfo(new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.STOPPED, 0, null)); - - mMediaController.refresh(); - - scheduleProgressUpdate(); - } - - @Override - protected void onResume() { - super.onResume(); - if (mVideoInfo.state == PlayerState.FINISHED) finish(); - if (mMediaRouteController == null) return; - - // Lifetime of the media element is bound to that of the {@link MediaStateListener} - // of the {@link MediaRouteController}. - RecordCastAction.recordFullscreenControlsShown( - mMediaRouteController.getMediaStateListener() != null); - - mMediaRouteController.prepareMediaRoute(); - - ImageView iv = (ImageView) findViewById(R.id.cast_background_image); - if (iv == null) return; - Bitmap posterBitmap = mMediaRouteController.getPoster(); - if (posterBitmap != null) iv.setImageBitmap(posterBitmap); - iv.setImageAlpha(POSTER_IMAGE_ALPHA); - } - - @Override - protected void onDestroy() { - cleanup(); - super.onDestroy(); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - int keyCode = event.getKeyCode(); - if ((keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP) - || mVideoInfo.state == PlayerState.FINISHED) { - return super.dispatchKeyEvent(event); - } - - return handleVolumeKeyEvent(mMediaRouteController, event); - } - - private void cleanup() { - if (mHandler != null) mHandler.removeCallbacks(mProgressUpdater); - if (mMediaRouteController != null) mMediaRouteController.removeUiListener(this); - mMediaRouteController = null; - mProgressUpdater = null; - } - - /** - * Sets the remote's video information to display. - */ - private final void setVideoInfo(RemoteVideoInfo videoInfo) { - if ((mVideoInfo == null) ? (videoInfo == null) : mVideoInfo.equals(videoInfo)) return; - - mVideoInfo = videoInfo; - onVideoInfoChanged(); - } - - private void scheduleProgressUpdate() { - mHandler.removeCallbacks(mProgressUpdater); - if (mMediaRouteController.isPlaying()) { - mHandler.post(mProgressUpdater); - } - } - - /** - * Sets the name to display for the device. - */ - private void setScreenName(String screenName) { - if (TextUtils.equals(mScreenName, screenName)) return; - - mScreenName = screenName; - onScreenNameChanged(); - } - - private void onVideoInfoChanged() { - updateUi(); - } - - private void onScreenNameChanged() { - updateUi(); - } - - private void updateUi() { - if (mMediaController == null || mMediaRouteController == null) return; - - String deviceName = mMediaRouteController.getRouteName(); - String castText = ""; - if (deviceName != null) { - castText = getResources().getString(R.string.cast_casting_video, deviceName); - } - TextView castTextView = (TextView) findViewById(R.id.cast_screen_title); - castTextView.setText(castText); - - mMediaController.refresh(); - } - - @Override - public void onRouteSelected(String name, MediaRouteController mediaRouteController) { - setScreenName(name); - } - - @Override - public void onRouteUnselected(MediaRouteController mediaRouteController) { - finish(); - } - - @Override - public void onPrepared(MediaRouteController mediaRouteController) { - // No implementation. - } - - @Override - public void onError(int error, String message) { - if (error == CastMediaControlIntent.ERROR_CODE_SESSION_START_FAILED) finish(); - } - - @Override - public void onPlaybackStateChanged(@PlayerState int newState) { - RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); - videoInfo.state = newState; - setVideoInfo(videoInfo); - - scheduleProgressUpdate(); - - if (newState == PlayerState.FINISHED || newState == PlayerState.INVALIDATED) { - // If we are switching to a finished state, stop the notifications. - finish(); - } - } - - @Override - public void onDurationUpdated(long durationMillis) { - RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); - videoInfo.durationMillis = durationMillis; - setVideoInfo(videoInfo); - } - - @Override - public void onPositionChanged(long positionMillis) { - RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); - videoInfo.currentTimeMillis = positionMillis; - setVideoInfo(videoInfo); - } - - @Override - public void onTitleChanged(String title) { - RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); - videoInfo.title = title; - setVideoInfo(videoInfo); - } - - /** - * Modify remote volume by handling volume keys. - * - * @param controller The remote controller through which the volume will be modified. - * @param event The key event. Its keycode needs to be either {@code KEYCODE_VOLUME_DOWN} or - * {@code KEYCODE_VOLUME_UP} otherwise this method will return false. - * @return True if the event is handled. - */ - private boolean handleVolumeKeyEvent(MediaRouteController controller, KeyEvent event) { - if (!controller.isBeingCast()) return false; - - int action = event.getAction(); - int keyCode = event.getKeyCode(); - // Intercept the volume keys to affect only remote volume. - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_DOWN: - if (action == KeyEvent.ACTION_DOWN) controller.setRemoteVolume(-1); - return true; - case KeyEvent.KEYCODE_VOLUME_UP: - if (action == KeyEvent.ACTION_DOWN) controller.setRemoteVolume(1); - return true; - default: - return false; - } - } - - /** - * Launches the ExpandedControllerActivity as a new task. - * - * @param context the Context to start this activity within. - */ - public static void startActivity(Context context) { - if (context == null) return; - - Intent intent = new Intent(context, ExpandedControllerActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/FullscreenMediaRouteButton.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/FullscreenMediaRouteButton.java deleted file mode 100644 index 376859f5..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/FullscreenMediaRouteButton.java +++ /dev/null
@@ -1,71 +0,0 @@ -// Copyright 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. - -package org.chromium.chrome.browser.media.remote; - -import android.content.Context; -import android.support.v7.app.MediaRouteButton; -import android.util.AttributeSet; -import android.view.View; - -/** - * Cast button that wraps around a MediaRouteButton. We show the button only if there are available - * cast devices. - */ -public class FullscreenMediaRouteButton extends MediaRouteButton { - - // Are we in the time window when the button should become visible if there're routes? - private boolean mVisibilityRequested; - - /** - * The constructor invoked when inflating the button. - */ - public FullscreenMediaRouteButton(Context context, AttributeSet attributeSet) { - super(context, attributeSet); - mVisibilityRequested = false; - } - - /** - * Set the necessary state for the button to work. - */ - /** - * Set the necessary state for the button to work - * @param controller the MediaRouteController controlling the route - */ - public void initialize(MediaRouteController controller) { - setRouteSelector(controller.buildMediaRouteSelector()); - setDialogFactory(new MediaRouteControllerDialogFactory(controller.getMediaStateListener())); - } - - @Override - public void setEnabled(boolean enabled) { - // TODO(aberent) not sure if this is still used, and in particular if mVisibilityRequest - // is still used. - - // We need to check if the button was in the same state before to avoid doing anything, - // but we also need to update the current state for {@link #setButtonVisibility} to work. - boolean wasEnabled = isEnabled(); - super.setEnabled(enabled); - - if (wasEnabled == enabled) return; - - if (enabled && mVisibilityRequested) { - setButtonVisibility(View.VISIBLE); - } else { - setVisibility(View.GONE); - } - } - - private void setButtonVisibility(int visibility) { - // If the button is being set to visible, first make sure that it can even cast - // to anything before making it actually visible. - if (visibility == View.VISIBLE) { - setVisibility(isEnabled() ? View.VISIBLE : View.GONE); - } else { - setVisibility(visibility); - } - } - -} -
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteChooserDialogFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteChooserDialogFactory.java deleted file mode 100644 index ba95e81..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteChooserDialogFactory.java +++ /dev/null
@@ -1,146 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.app.MediaRouteChooserDialog; -import android.support.v7.app.MediaRouteChooserDialogFragment; -import android.support.v7.app.MediaRouteDialogFactory; -import android.support.v7.media.MediaRouter; -import android.view.View; -import android.widget.FrameLayout; - -import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener; - -/** - * The Chrome implementation of the dialog factory so custom behavior can - * be injected for the disconnect button. - */ -public class MediaRouteChooserDialogFactory extends MediaRouteDialogFactory { - - private final MediaRouteController mController; - private final Context mContext; - private final MediaStateListener mPlayer; - - MediaRouteChooserDialogFactory(MediaStateListener player, MediaRouteController controller, - Context context) { - mPlayer = player; - mController = controller; - mContext = context; - } - - private static class SystemVisibilitySaver { - private int mSystemVisibility; - private boolean mRestoreSystemVisibility; - - void saveSystemVisibility(Activity activity) { - // If we are in fullscreen we may have also have hidden the system UI. This - // is overridden when we display the dialog. Save the system UI visibility - // state so we can restore it. - FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView(); - mSystemVisibility = decor.getSystemUiVisibility(); - mRestoreSystemVisibility = ( - (mSystemVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0); - } - - void restoreSystemVisibility(Activity activity) { - if (mRestoreSystemVisibility) { - FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView(); - // In some cases we come out of fullscreen before closing this dialog. In these - // cases we don't want to restore the system UI visibility state. - int systemVisibility = decor.getSystemUiVisibility(); - if ((systemVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { - decor.setSystemUiVisibility(mSystemVisibility); - } - } - } - } - - /** - * A dialog fragment for choosing a media route that saves system visibility for handling - * fullscreen state of Chrome correctly. Needs to be a named public static class, see - * https://crbug.com/618993. - */ - public static final class Fragment extends MediaRouteChooserDialogFragment { - final Handler mHandler = new Handler(); - final MediaRouteController mController; - final MediaStateListener mPlayer; - final SystemVisibilitySaver mVisibilitySaver = new SystemVisibilitySaver(); - boolean mCancelled; - Context mContext; - - // The class has to be a public static class with a zero-argument constructor. - // Since we can't pass any callbacks to the fragment easily, just close the dialog. - // See https://crbug.com/618993. - public Fragment() { - mHandler.post(new Runnable() { - @Override - public void run() { - Fragment.this.dismiss(); - } - }); - mController = null; - mPlayer = null; - } - - @SuppressLint("ValidFragment") - Fragment(MediaRouteController controller, MediaStateListener player) { - mController = controller; - mPlayer = player; - } - - @Override - public MediaRouteChooserDialog onCreateChooserDialog( - Context context, Bundle savedInstanceState) { - mVisibilitySaver.saveSystemVisibility(getActivity()); - mContext = context; - return new MediaRouteChooserDialog(context) { - @Override - // Returns true if the route should be shown to the user. - public boolean onFilterRoute(MediaRouter.RouteInfo route) { - return super.onFilterRoute(route) && !mController.shouldFilterOutRoute(route); - } - }; - } - - @Override - public void onStop() { - super.onStop(); - mVisibilitySaver.restoreSystemVisibility(getActivity()); - } - - @Override - public void onCancel(DialogInterface dialog) { - mCancelled = true; - - super.onCancel(dialog); - } - - @Override - public void onDismiss(DialogInterface dialog) { - super.onDismiss(dialog); - - if (mCancelled) { - if (mPlayer != null) mPlayer.onRouteDialogCancelled(); - return; - } - - if (mController != null) { - MediaRouter router = MediaRouter.getInstance(mContext); - mController.onRouteSelected(mPlayer, router, router.getSelectedRoute()); - } - } - } - - @Override - public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() { - return new Fragment(mController, mPlayer); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteController.java deleted file mode 100644 index e06071fc..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteController.java +++ /dev/null
@@ -1,370 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.graphics.Bitmap; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; - -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; - -/** - * Each MediaRouteController controls the routes to devices which support remote playback of - * particular categories of Media elements (e.g. all YouTube media elements, all media elements - * with simple http source URLs). The MediaRouteController is responsible for configuring - * and controlling remote playback of the media elements it supports. - */ -public interface MediaRouteController { - /** - * Listener for events that are relevant to the state of the media and the media controls - */ - public interface MediaStateListener { - /** - * Called when the first route becomes available, or the last route - * is removed. - * @param available whether routes are available. - */ - void onRouteAvailabilityChanged(boolean available); - - /** - * Called when the {@link MediaRouteChooserDialog} is closed with no device selected. - */ - void onRouteDialogCancelled(); - - /** - * Called when an error is detected by the media route controller - */ - void onError(); - - /** - * Called when a seek completes on the current route - */ - void onSeekCompleted(); - - /** - * Called when the current route is unselected - */ - void onRouteUnselected(); - - /** - * Called when the playback state changes (e.g. from Playing to Paused) - * @param newState the new playback state - */ - void onPlaybackStateChanged(@PlayerState int newState); - - String getTitle(); - - Bitmap getPosterBitmap(); - - void pauseLocal(); - - long getLocalPosition(); - - /** - * Tells the rest of Chrome that we are starting to cast, so that user inputs control cast - * in place of local playback - */ - void onCastStarting(String routeName); - - /** - * Tells the rest of Chrome that we're connected to the Cast device and about to resume - * playback. - */ - void onCastStarted(); - - /** - * Tells the rest of Chrome that we are no longer casting the video. - */ - void onCastStopping(); - - /** - * @return the source URL - */ - String getSourceUrl(); - - /** - * @return the Cookies - */ - String getCookies(); - - /** - * @return the frame URL - */ - String getFrameUrl(); - - /** - * @return the start position - */ - long getStartPositionMillis(); - - /** - * @return true if the user has pressed the pause button (or requested pause some other way) - */ - boolean isPauseRequested(); - - /** - * @return true if the user has requested a seek - */ - boolean isSeekRequested(); - - /** - * @return the requested seek location. Only meaningful if isSeekRequested is true. - */ - long getSeekLocation(); - } - - /** - * Listener for events that are relevant to the Browser UI. - */ - public interface UiListener { - - /** - * Called when a new route is selected - * @param name the name of the new route - * @param mediaRouteController the controller that selected the route - */ - void onRouteSelected(String name, MediaRouteController mediaRouteController); - - /** - * Called when the current route is unselected - * @param mediaRouteController the controller that had the route. - */ - void onRouteUnselected(MediaRouteController mediaRouteController); - - /** - * Called when the current route is ready to be used - * @param mediaRouteController the controller that has the route. - */ - void onPrepared(MediaRouteController mediaRouteController); - - /** - * Called when an error is detected by the controller - * @param errorType One of the error types from CastMediaControlIntent - * @param message The message for the error - */ - void onError(int errorType, String message); - - /** - * Called when the Playback state has changed (e.g. from playing to paused) - * @param newState the new state - */ - void onPlaybackStateChanged(@PlayerState int newState); - - /** - * Called when the duration of the currently playing video changes. - * @param durationMillis the new duration in ms. - */ - void onDurationUpdated(long durationMillis); - - /** - * Called when the media route controller receives new information about the - * current position in the video. - * @param positionMillis the current position in the video in ms. - */ - void onPositionChanged(long positionMillis); - - /** - * Called if the title of the video changes - * @param title the new title - */ - void onTitleChanged(String title); - } - - /** - * Interface for returning the result of checking whether the media element is playable - * remotely. - */ - static interface MediaValidationCallback { - /** - * Function to deliver the result - * @param isPlayable true if the media element is playable, false if not - * @param revisedSourceUrl The source url to send to the remote device - * @param revisedFrameUrl The frame url to send to the remote device - */ - void onResult(boolean isPlayable, String revisedSourceUrl, String revisedFrameUrl); - } - /** - * Scan routes, and set up the MediaRouter object. This is called at every time we need to reset - * the state. Because of that, this function is idempotent. If that changes in the future, where - * this function gets called needs to be re-evaluated. - * - * @return false if device doesn't support cast, true otherwise. - */ - boolean initialize(); - - /** - * Can this mediaRouteController handle a media element? - * @param sourceUrl the source - * @param frameUrl - * @return true if it can, false if it can't. - */ - boolean canPlayMedia(String sourceUrl, String frameUrl); - - /** - * @return A new MediaRouteSelector filtering the remote playback devices from all the routes. - */ - MediaRouteSelector buildMediaRouteSelector(); - - /** - * @return Whether there're remote playback devices available. - */ - boolean isRemotePlaybackAvailable(); - - /** - * @return Whether the currently selected device supports remote playback - */ - boolean currentRouteSupportsRemotePlayback(); - - /** - * Setup this object to discover new routes and register the necessary players. - */ - void prepareMediaRoute(); - - /** - * Add a Listener that will listen to events from this object - * - * @param listener the Listener that will receive the events - */ - void addUiListener(UiListener listener); - - /** - * Removes a Listener from this object - * - * @param listener the Listener to remove - */ - void removeUiListener(UiListener listener); - - /** - * @return The currently selected route's friendly name, or null if there is none selected - */ - String getRouteName(); - - /** - * @return true if this is currently using the default route, false if not. - */ - boolean routeIsDefaultRoute(); - - /** - * Sets the remote volume of the current route. - * - * @param delta The delta value in arbitrary "Android Volume Units". - */ - void setRemoteVolume(int delta); - - /** - * Resume paused playback of the current video. - */ - void resume(); - - /** - * Pauses the currently playing video if any. - */ - void pause(); - - /** - * Returns the current remote playback position. Estimates the current position by using the - * last known position and the current time. - * - * TODO(avayvod): Send periodic status update requests to update the position once in several - * seconds or so. - * - * @return The current position of the remote playback in milliseconds. - */ - long getPosition(); - - /** - * @return The stream duration in milliseconds. - */ - long getDuration(); - - /** - * @return Whether the video is currently being played. - */ - boolean isPlaying(); - - /** - * @return Whether the video is being cast (any of playing/paused/loading/stopped). - */ - boolean isBeingCast(); - - /** - * Initiates a seek request for the remote playback device to the specified position. - * - * @param msec The position to seek to, in milliseconds. - */ - void seekTo(long msec); - - /** - * Stop the current remote playback completely and release all resources. - */ - void release(); - - /** - * @param player - the current player using this media route controller. - */ - void setMediaStateListener(MediaStateListener listener); - - /** - * @return the current VideoStateListener - */ - MediaStateListener getMediaStateListener(); - - @VisibleForTesting - @PlayerState - int getDisplayedPlayerState(); - - /** - * Remove an existing media state listener - * @param listener - */ - void removeMediaStateListener(MediaStateListener listener); - - /** - * Add a media state listener - * @param listener - */ - void addMediaStateListener(MediaStateListener listener); - - /** - * Get the poster for the video, if any - * @return the poster bitmap, or Null. - */ - Bitmap getPoster(); - - /** - * Used to filter out routes in the MediaRouteChooserDialog. - * Overridden in some downstream code. - * @param route the route to be filtered. - * @return true if the route should be excluded, false if it should be shown to the user. - */ - boolean shouldFilterOutRoute(MediaRouter.RouteInfo route); - - /** - * Called when a new route has been selected - * @param player The player {@link MediaStateListener} that initiated the connection - * @param router The MediaRouter. - * @param route The selected route. - */ - void onRouteSelected(MediaStateListener player, MediaRouter router, RouteInfo route); - - /** - * Potentially asynchronous check of whether the media element is playable on remote players. - * @param sourceUrl the URL of the media element - * @param frameUrl the URL of the frame - * @param cookies the cookies for the media element - * @param userAgent the user agent - * @param callback the callback through which the result will be returned. The callback will be - * called either from within the call, or later on the UI thread. - */ - void checkIfPlayableRemotely(String sourceUrl, String frameUrl, String cookies, - String userAgent, MediaValidationCallback callback); - - /** - * @return The Uri of the currently playing video - */ - @VisibleForTesting String getUriPlaying(); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteControllerDialogFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteControllerDialogFactory.java deleted file mode 100644 index 2b9ac35..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaRouteControllerDialogFactory.java +++ /dev/null
@@ -1,114 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.app.MediaRouteControllerDialog; -import android.support.v7.app.MediaRouteControllerDialogFragment; -import android.support.v7.app.MediaRouteDialogFactory; -import android.view.View; -import android.widget.FrameLayout; - -import org.chromium.base.Log; -import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener; - -/** - * The Chrome implementation of the dialog factory so custom behavior can - * be injected for the disconnect button. - */ -public class MediaRouteControllerDialogFactory extends MediaRouteDialogFactory { - private static final String TAG = "MRCtrlDlg"; - - private final MediaStateListener mPlayer; - - MediaRouteControllerDialogFactory(MediaStateListener player) { - mPlayer = player; - } - - private static class SystemVisibilitySaver { - private int mSystemVisibility; - private boolean mRestoreSystemVisibility; - - void saveSystemVisibility(Activity activity) { - // If we are in fullscreen we may have also have hidden the system UI. This - // is overridden when we display the dialog. Save the system UI visibility - // state so we can restore it. - FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView(); - mSystemVisibility = decor.getSystemUiVisibility(); - mRestoreSystemVisibility = ( - (mSystemVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0); - } - - void restoreSystemVisibility(Activity activity) { - if (mRestoreSystemVisibility) { - FrameLayout decor = (FrameLayout) activity.getWindow().getDecorView(); - // In some cases we come out of fullscreen before closing this dialog. In these - // cases we don't want to restore the system UI visibility state. - int systemVisibility = decor.getSystemUiVisibility(); - if ((systemVisibility & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { - decor.setSystemUiVisibility(mSystemVisibility); - } - } - } - } - - /** - * A dialog fragment for controlling a media route that saves system visibility for - * handling fullscreen state of Chrome correctly. Needs to be a named public static class, - * see https://crbug.com/618993. - */ - public static final class Fragment extends MediaRouteControllerDialogFragment { - final Handler mHandler = new Handler(); - final SystemVisibilitySaver mVisibilitySaver = new SystemVisibilitySaver(); - final MediaStateListener mPlayer; - - // The class has to be a public static class with a zero-argument constructor. - // Since we can't pass any callbacks to the fragment easily, just close the dialog. - // See https://crbug.com/618993. - public Fragment() { - mHandler.post(new Runnable() { - @Override - public void run() { - Fragment.this.dismiss(); - } - }); - mPlayer = null; - } - - @SuppressLint("ValidFragment") - Fragment(MediaStateListener player) { - mPlayer = player; - } - - @Override - public Dialog onCreateDialog(Bundle saved) { - mVisibilitySaver.saveSystemVisibility(getActivity()); - return new MediaRouteControllerDialog(getActivity()); - } - - @Override - public void onStop() { - super.onStop(); - mVisibilitySaver.restoreSystemVisibility(getActivity()); - } - - @Override - public void onDismiss(DialogInterface dialog) { - Log.d(TAG, "onDismiss " + mPlayer); - super.onDismiss(dialog); - if (mPlayer != null) mPlayer.onRouteDialogCancelled(); - } - } - - @Override - public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() { - return new Fragment(mPlayer); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaUrlResolver.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaUrlResolver.java deleted file mode 100644 index ff91142..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/MediaUrlResolver.java +++ /dev/null
@@ -1,288 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.net.Uri; -import android.support.annotation.IntDef; -import android.text.TextUtils; - -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.metrics.RecordHistogram; -import org.chromium.base.task.AsyncTask; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.net.HttpURLConnection; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLStreamHandler; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -/** - * Resolves the final URL if it's a redirect. Works asynchronously, uses HTTP - * HEAD request to determine if the URL is redirected. - */ -public class MediaUrlResolver extends AsyncTask<MediaUrlResolver.Result> { - // Cast.Sender.UrlResolveResult UMA histogram values; must match values of - // RemotePlaybackUrlResolveResult in histograms.xml. Do not change these values, as they are - // being used in UMA. - @IntDef({ResolveResult.SUCCESS, ResolveResult.MALFORMED_URL, ResolveResult.NO_CORS, - ResolveResult.INCOMPATIBLE_CORS, ResolveResult.SERVER_ERROR, - ResolveResult.NETWORK_ERROR, ResolveResult.UNSUPPORTED_MEDIA, - ResolveResult.HUC_EXCEPTION}) - @Retention(RetentionPolicy.SOURCE) - public @interface ResolveResult { - int SUCCESS = 0; - int MALFORMED_URL = 1; - int NO_CORS = 2; - int INCOMPATIBLE_CORS = 3; - int SERVER_ERROR = 4; - int NETWORK_ERROR = 5; - int UNSUPPORTED_MEDIA = 6; - int HUC_EXCEPTION = 7; - int NUM_ENTRIES = 8; - } - - // Acceptal response codes for URL resolving request. - private static final Integer[] SUCCESS_RESPONSE_CODES = { - // Request succeeded. - HttpURLConnection.HTTP_OK, - HttpURLConnection.HTTP_PARTIAL, - - // HttpURLConnection only follows up to 5 redirects, this response is unlikely but possible. - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP, - }; - - /** - * The interface to get the initial URI with cookies from and pass the final - * URI to. - */ - public interface Delegate { - /** - * @return the original URL to resolve. - */ - Uri getUri(); - - /** - * @return the cookies to fetch the URL with. - */ - String getCookies(); - - /** - * Passes the resolved URL to the delegate. - * - * @param uri the resolved URL. - */ - void deliverResult(Uri uri, boolean palyable); - } - - - protected static final class Result { - private final Uri mUri; - private final boolean mPlayable; - - public Result(Uri uri, boolean playable) { - mUri = uri; - mPlayable = playable; - } - - public Uri getUri() { - return mUri; - } - - public boolean isPlayable() { - return mPlayable; - } - } - - private static final String TAG = "MediaFling"; - - private static final String COOKIES_HEADER_NAME = "Cookies"; - private static final String USER_AGENT_HEADER_NAME = "User-Agent"; - private static final String ORIGIN_HEADER_NAME = "Origin"; - private static final String RANGE_HEADER_NAME = "Range"; - private static final String CORS_HEADER_NAME = "Access-Control-Allow-Origin"; - - private static final String CHROMECAST_ORIGIN = "https://www.gstatic.com"; - - // Media types supported for cast, see - // media/base/container_names.h for the actual enum where these are defined. - // See https://developers.google.com/cast/docs/media#media-container-formats for the formats - // supported by Cast devices. - @IntDef({MediaType.UNKNOWN, MediaType.AAC, MediaType.HLS, MediaType.MP3, MediaType.MPEG4, - MediaType.OGG, MediaType.WAV, MediaType.WEBM, MediaType.DASH, MediaType.SMOOTHSTREAM}) - @Retention(RetentionPolicy.SOURCE) - private @interface MediaType { - int UNKNOWN = 0; - int AAC = 1; - int HLS = 22; - int MP3 = 26; - int MPEG4 = 29; - int OGG = 30; - int WAV = 35; - int WEBM = 36; - int DASH = 38; - int SMOOTHSTREAM = 39; - } - - // We don't want to necessarily fetch the whole video but we don't want to miss the CORS header. - // Assume that 64k should be more than enough to keep all the headers. - private static final String RANGE_HEADER_VALUE = "bytes=0-65536"; - - private final Delegate mDelegate; - - private final String mUserAgent; - private final URLStreamHandler mStreamHandler; - - /** - * The constructor - * @param delegate The customer for this URL resolver. - * @param userAgent The browser user agent - */ - public MediaUrlResolver(Delegate delegate, String userAgent) { - this(delegate, userAgent, null); - } - - @VisibleForTesting - MediaUrlResolver(Delegate delegate, String userAgent, URLStreamHandler streamHandler) { - mDelegate = delegate; - mUserAgent = userAgent; - mStreamHandler = streamHandler; - } - - @Override - protected MediaUrlResolver.Result doInBackground() { - Uri uri = mDelegate.getUri(); - if (uri == null || uri.equals(Uri.EMPTY)) { - return new MediaUrlResolver.Result(Uri.EMPTY, false); - } - String cookies = mDelegate.getCookies(); - - Map<String, List<String>> headers = null; - HttpURLConnection urlConnection = null; - try { - URL requestUrl = new URL(null, uri.toString(), mStreamHandler); - urlConnection = (HttpURLConnection) requestUrl.openConnection(); - if (!TextUtils.isEmpty(cookies)) { - urlConnection.setRequestProperty(COOKIES_HEADER_NAME, cookies); - } - - // Pretend that this is coming from the Chromecast. - urlConnection.setRequestProperty(ORIGIN_HEADER_NAME, CHROMECAST_ORIGIN); - urlConnection.setRequestProperty(USER_AGENT_HEADER_NAME, mUserAgent); - if (!isEnhancedMedia(uri)) { - // Manifest files are typically smaller than 64K so range request can fail. - urlConnection.setRequestProperty(RANGE_HEADER_NAME, RANGE_HEADER_VALUE); - } - - // This triggers resolving the URL and receiving the headers. - headers = urlConnection.getHeaderFields(); - - uri = Uri.parse(urlConnection.getURL().toString()); - - // If server's response is not valid, don't try to fling the video. - int responseCode = urlConnection.getResponseCode(); - if (!Arrays.asList(SUCCESS_RESPONSE_CODES).contains(responseCode)) { - recordResultHistogram(ResolveResult.SERVER_ERROR); - Log.e(TAG, "Server response is not valid: %d", responseCode); - uri = Uri.EMPTY; - } - } catch (IOException | IllegalArgumentException e) { - // IllegalArgumentException for SSL issue (https://b/78588631). - recordResultHistogram(ResolveResult.NETWORK_ERROR); - Log.e(TAG, "Failed to fetch the final url", e); - uri = Uri.EMPTY; - } catch (ArrayIndexOutOfBoundsException | NullPointerException e) { - recordResultHistogram(ResolveResult.HUC_EXCEPTION); - Log.e(TAG, "Threading issue with HUC, see https://crbug.com/754480", e); - uri = Uri.EMPTY; - } catch (RuntimeException e) { - if (e.getCause() instanceof URISyntaxException) { - Log.e(TAG, "Invalid URL format", e); - uri = Uri.EMPTY; - } else { - throw e; - } - } finally { - if (urlConnection != null) urlConnection.disconnect(); - } - return new MediaUrlResolver.Result(uri, canPlayMedia(uri, headers)); - } - - @Override - protected void onPostExecute(MediaUrlResolver.Result result) { - mDelegate.deliverResult(result.getUri(), result.isPlayable()); - } - - private boolean canPlayMedia(Uri uri, Map<String, List<String>> headers) { - if (uri == null || uri.equals(Uri.EMPTY)) { - recordResultHistogram(ResolveResult.MALFORMED_URL); - return false; - } - - if (headers != null && headers.containsKey(CORS_HEADER_NAME)) { - // Check that the CORS data is valid for Chromecast - List<String> corsData = headers.get(CORS_HEADER_NAME); - if (corsData.isEmpty() || (!corsData.get(0).equals("*") - && !corsData.get(0).equals(CHROMECAST_ORIGIN))) { - recordResultHistogram(ResolveResult.INCOMPATIBLE_CORS); - return false; - } - } else if (isEnhancedMedia(uri)) { - // HLS media requires CORS headers. - // TODO(avayvod): it actually requires CORS on the final video URLs vs the manifest. - // Clank assumes that if CORS is set for the manifest it's set for everything but - // it not necessary always true. See b/19138712 - Log.d(TAG, "HLS stream without CORS header: %s", uri); - recordResultHistogram(ResolveResult.NO_CORS); - return false; - } - - if (getMediaType(uri) == MediaType.UNKNOWN) { - Log.d(TAG, "Unsupported media container format: %s", uri); - recordResultHistogram(ResolveResult.UNSUPPORTED_MEDIA); - return false; - } - - recordResultHistogram(ResolveResult.SUCCESS); - return true; - } - - private boolean isEnhancedMedia(Uri uri) { - int mediaType = getMediaType(uri); - return mediaType == MediaType.HLS || mediaType == MediaType.DASH - || mediaType == MediaType.SMOOTHSTREAM; - } - - @VisibleForTesting - void recordResultHistogram(@ResolveResult int result) { - RecordHistogram.recordEnumeratedHistogram( - "Cast.Sender.UrlResolveResult", result, ResolveResult.NUM_ENTRIES); - } - - static @MediaType int getMediaType(Uri uri) { - String path = uri.getPath(); - - if (path == null) return MediaType.UNKNOWN; - - path = path.toLowerCase(Locale.US); - if (path.endsWith(".m3u8")) return MediaType.HLS; - if (path.endsWith(".mp4")) return MediaType.MPEG4; - if (path.endsWith(".mpd")) return MediaType.DASH; - if (path.endsWith(".ism")) return MediaType.SMOOTHSTREAM; - if (path.endsWith(".m4a") || path.endsWith(".aac")) return MediaType.AAC; - if (path.endsWith(".mp3")) return MediaType.MP3; - if (path.endsWith(".wav")) return MediaType.WAV; - if (path.endsWith(".webm")) return MediaType.WEBM; - if (path.endsWith(".ogg")) return MediaType.OGG; - return MediaType.UNKNOWN; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/PositionExtrapolator.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/PositionExtrapolator.java deleted file mode 100644 index 455401b..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/PositionExtrapolator.java +++ /dev/null
@@ -1,122 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import android.os.SystemClock; - -/** - * Class for extrapolating current playback position. The class occasionally receives updated - * playback position information from the MediaRoute, and extrapolates the current playback - * position. - */ -public class PositionExtrapolator { - private static final String TAG = "MediaFling"; - - private long mDuration; - private long mLastKnownPosition; - private long mTimestamp; - private boolean mIsPlaying; - - public PositionExtrapolator() { - mDuration = 0; - mLastKnownPosition = 0; - mTimestamp = 0; - mIsPlaying = false; - } - - /** - * Clears the duration and timestamp. - */ - public void clear() { - // Note: do not clear the stream position, since this is still needed so - // that we can reset the local stream position to match. - mDuration = 0; - mTimestamp = 0; - } - - /** - * Update the extrapolator with the latest position info. - * @param duration The new duration. - * @param position The new playback position. - * @param timestamp The time stamp of this info, must be directly or indirectly aquired via - * {@link SystemClock.elapsedRealtime()}. The time stamp from the Cast receiver uses - * elapsedRealtime, so it can be used here. Don't use {@link SystemClock.uptimeMillis()} since - * it doesn't include the device sleep time. - */ - public void onPositionInfoUpdated( - long duration, long position, long timestamp) { - mDuration = Math.max(duration, 0); - mLastKnownPosition = Math.min(mDuration, Math.max(position, 0)); - mTimestamp = timestamp; - } - - /** - * Must be called whenever the remote playback is paused. If the playback - * state changes from playing to paused, the last known stream position will be updated by the - * extrapolated value. - */ - public void onPaused() { - if (!mIsPlaying) return; - - long elapsedTime = SystemClock.elapsedRealtime(); - onPositionInfoUpdated(mDuration, getPositionWithElapsedTime(elapsedTime), elapsedTime); - - mIsPlaying = false; - } - - /** - * Must be called whenever the remote playback resumes. If the playback state changes from - * paused to playing, the timestamp will be updated by the current {@link - * SystemClock.elapsedRealtime()}. - */ - public void onResumed() { - if (mIsPlaying) return; - - onPositionInfoUpdated(mDuration, mLastKnownPosition, SystemClock.elapsedRealtime()); - mIsPlaying = true; - } - - /** - * Must be called whenever the remote playback is finished. The last known position will be - * updated by the duration. - */ - public void onFinished() { - onPositionInfoUpdated(mDuration, mDuration, SystemClock.elapsedRealtime()); - mIsPlaying = false; - } - - /** - * Must be called whenever the remote playback is seeking. The last known position will be - * updated by the position to be seeked to, and the extrapolator will assume the playback is - * paused during seeking. - * @param position The position to seek to. - */ - public void onSeek(long position) { - onPositionInfoUpdated(mDuration, position, SystemClock.elapsedRealtime()); - mIsPlaying = false; - } - - /** - * @return The current playback position. - */ - public long getPosition() { - return getPositionWithElapsedTime(SystemClock.elapsedRealtime()); - } - - /** - * @return The duration of the remote media. - */ - public long getDuration() { - return mDuration; - } - - private long getPositionWithElapsedTime(long elapsedTime) { - if ((mTimestamp == 0) || !mIsPlaying || mLastKnownPosition >= mDuration) { - return mLastKnownPosition; - } - - return Math.min(mLastKnownPosition + (elapsedTime - mTimestamp), mDuration); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerController.java deleted file mode 100644 index 2008e75..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerController.java +++ /dev/null
@@ -1,346 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v7.app.MediaRouteChooserDialogFragment; -import android.support.v7.app.MediaRouteControllerDialogFragment; -import android.support.v7.app.MediaRouteDialogFactory; - -import com.google.android.gms.cast.CastMediaControlIntent; - -import org.chromium.base.ApplicationStatus; -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.ui.widget.Toast; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * The singleton responsible managing the global resources for remote media playback (cast) - */ -public class RemoteMediaPlayerController implements MediaRouteController.UiListener { - // Singleton instance of the class. May only be accessed from UI thread. - @SuppressLint("StaticFieldLeak") - private static RemoteMediaPlayerController sInstance; - - private static final String TAG = "MediaFling"; - - private static final String DEFAULT_CASTING_MESSAGE = "Casting to Chromecast"; - - private CastNotificationControl mNotificationControl; - - private Context mCastContextApplicationContext; - // The Activity that was in the foreground when the video was cast. - private WeakReference<Activity> mChromeVideoActivity; - - private List<MediaRouteController> mMediaRouteControllers; - - // points to mDefaultRouteSelector, mYouTubeRouteSelector or null - private MediaRouteController mCurrentRouteController; - - // This is a key for meta-data in the package manifest. - private static final String REMOTE_MEDIA_PLAYERS_KEY = - "org.chromium.content.browser.REMOTE_MEDIA_PLAYERS"; - - /** - * The private constructor to make sure the object is only created by the instance() method. - */ - private RemoteMediaPlayerController() { - mChromeVideoActivity = new WeakReference<Activity>(null); - mMediaRouteControllers = new ArrayList<MediaRouteController>(); - } - - /** - * @return The poster image for the currently playing remote video, null if there's none. - */ - public Bitmap getPoster() { - if (mCurrentRouteController == null) return null; - return mCurrentRouteController.getPoster(); - } - - /** - * The singleton instance access method for native objects. Must be called on the UI thread - * only. - */ - public static RemoteMediaPlayerController instance() { - ThreadUtils.assertOnUiThread(); - - if (sInstance == null) sInstance = new RemoteMediaPlayerController(); - if (sInstance.mChromeVideoActivity.get() == null) sInstance.linkToBrowserActivity(); - - return sInstance; - } - - /** - * Gets the MediaRouteController for a video, creating it if necessary. - * @param frameUrl The Url of the frame containing the video - * @return the MediaRouteController, or null if none. - */ - public MediaRouteController getMediaRouteController(String sourceUrl, String frameUrl) { - for (MediaRouteController controller: mMediaRouteControllers) { - if (controller.canPlayMedia(sourceUrl, frameUrl)) { - return controller; - } - } - return null; - } - - /** - * Gets the default MediaRouteController, creating it if necessary. - * @return the default MediaRouteController. - */ - public List<MediaRouteController> getMediaRouteControllers() { - return mMediaRouteControllers; - } - - /** - * Links this object to the Activity that owns the video, if it exists. - * - */ - private void linkToBrowserActivity() { - - Activity currentActivity = ApplicationStatus.getLastTrackedFocusedActivity(); - if (currentActivity != null) { - mChromeVideoActivity = new WeakReference<Activity>(currentActivity); - - mCastContextApplicationContext = currentActivity.getApplicationContext(); - createMediaRouteControllers(currentActivity); - } - } - - /** - * Create the mediaRouteControllers - * @param context - the current Android Context - */ - public void createMediaRouteControllers(Context context) { - // We only need to do this once - if (!mMediaRouteControllers.isEmpty()) return; - try { - ApplicationInfo ai = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - String classNameString = bundle.getString(REMOTE_MEDIA_PLAYERS_KEY); - - if (classNameString != null) { - String[] classNames = classNameString.split(","); - for (String className : classNames) { - Log.d(TAG, "Adding remote media route controller %s", className.trim()); - Class<?> mediaRouteControllerClass = Class.forName(className.trim()); - Object mediaRouteController = mediaRouteControllerClass.newInstance(); - mMediaRouteControllers.add((MediaRouteController) mediaRouteController); - } - } - } catch (NameNotFoundException | ClassNotFoundException | SecurityException - | InstantiationException | IllegalAccessException | IllegalArgumentException e) { - // Should never happen, implies corrupt AndroidManifest - Log.e(TAG, "Couldn't instatiate MediaRouteControllers", e); - assert false; - } - } - - private void onStateReset(MediaRouteController controller) { - - if (!controller.initialize()) return; - - mNotificationControl = CastNotificationControl.getOrCreate( - mChromeVideoActivity.get(), controller); - mNotificationControl.setPosterBitmap(getPoster()); - controller.prepareMediaRoute(); - - controller.addUiListener(this); - } - - /** - * Called when a lower layer requests that a video be cast. This will typically be a request - * from Blink when the cast button is pressed on the default video controls. - * @param player the player for which cast is being requested - * @param frameUrl the URL of the frame containing the video, needed for YouTube videos - */ - public void requestRemotePlayback( - MediaRouteController.MediaStateListener player, MediaRouteController controller) { - Activity currentActivity = ApplicationStatus.getLastTrackedFocusedActivity(); - mChromeVideoActivity = new WeakReference<Activity>(currentActivity); - - if (mCurrentRouteController != null && controller != mCurrentRouteController) { - mCurrentRouteController.release(); - } - - onStateReset(controller); - showMediaRouteDialog(player, controller, currentActivity); - - } - - /** - * Called when a lower layer requests control of a video that is being cast. - * @param player The player for which remote playback control is being requested. - */ - public void requestRemotePlaybackControl(MediaRouteController.MediaStateListener player) { - // Player should match currently remotely played item, but there - // can be a race between various - // ways that the a video can stop playing remotely. Check that the - // player is current, and ignore if not. - - if (mCurrentRouteController == null) return; - if (mCurrentRouteController.getMediaStateListener() != player) return; - - showMediaRouteControlDialog(player, ApplicationStatus.getLastTrackedFocusedActivity()); - } - - /** - * Called when a lower layer requests to stop casting the video. - * @param player The player to stop remote playback for. - */ - public void requestRemotePlaybackStop(MediaRouteController.MediaStateListener player) { - if (mCurrentRouteController == null) return; - if (mCurrentRouteController.getMediaStateListener() != player) return; - - mCurrentRouteController.release(); - } - - private void showMediaRouteDialog(MediaStateListener player, MediaRouteController controller, - Activity activity) { - - FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager(); - if (fm == null) { - throw new IllegalStateException("The activity must be a subclass of FragmentActivity"); - } - - MediaRouteDialogFactory factory = new MediaRouteChooserDialogFactory(player, controller, - activity); - - if (fm.findFragmentByTag( - "android.support.v7.mediarouter:MediaRouteChooserDialogFragment") != null) { - Log.w(TAG, "showDialog(): Route chooser dialog already showing!"); - return; - } - MediaRouteChooserDialogFragment f = factory.onCreateChooserDialogFragment(); - - f.setRouteSelector(controller.buildMediaRouteSelector()); - f.show(fm, "android.support.v7.mediarouter:MediaRouteChooserDialogFragment"); - } - - private void showMediaRouteControlDialog(MediaStateListener player, Activity activity) { - FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager(); - if (fm == null) { - throw new IllegalStateException("The activity must be a subclass of FragmentActivity"); - } - MediaRouteDialogFactory factory = new MediaRouteControllerDialogFactory(player); - - if (fm.findFragmentByTag( - "android.support.v7.mediarouter:MediaRouteControllerDialogFragment") != null) { - Log.w(TAG, "showDialog(): Route controller dialog already showing!"); - return; - } - MediaRouteControllerDialogFragment f = factory.onCreateControllerDialogFragment(); - - f.show(fm, "android.support.v7.mediarouter:MediaRouteControllerDialogFragment"); - } - - /** - * @return the currently playing MediaRouteController - */ - public MediaRouteController getCurrentlyPlayingMediaRouteController() { - return mCurrentRouteController; - } - - /** - * Set the current MediaRouteController - * @param controller the controller - */ - public void setCurrentMediaRouteController(MediaRouteController controller) { - mCurrentRouteController = controller; - } - - private CastNotificationControl getNotificationControl() { - return mNotificationControl; - } - - @Override - public void onPrepared(MediaRouteController mediaRouteController) { - } - - @Override - public void onPlaybackStateChanged(@PlayerState int newState) {} - - @Override - public void onError(int error, String errorMessage) { - if (error == CastMediaControlIntent.ERROR_CODE_SESSION_START_FAILED) { - showMessageToast(errorMessage); - } - } - - @Override - public void onDurationUpdated(long durationMillis) {} - - @Override - public void onPositionChanged(long positionMillis) {} - - @Override - public void onTitleChanged(String title) {} - - @Override - public void onRouteSelected(String routeName, MediaRouteController mediaRouteController) { - if (mCurrentRouteController != mediaRouteController) { - mCurrentRouteController = mediaRouteController; - resetPlayingVideo(); - } - } - - /** - * Gets some text to tell the user that the video is being cast. - * @param routeName The name of the route on which the video is being cast. - * @return A String to be shown to the user. - */ - public String getCastingMessage(String routeName) { - String castingMessage = DEFAULT_CASTING_MESSAGE; - if (mCastContextApplicationContext != null) { - castingMessage = mCastContextApplicationContext.getString( - R.string.cast_casting_video, routeName); - } - return castingMessage; - } - - // Note that, after switching MediaRouteControllers onRouteUnselected may be called for - // the old media route controller, so this should not do anything to - // mCurrentRouteController - @Override - public void onRouteUnselected(MediaRouteController mediaRouteController) { - if (mediaRouteController == mCurrentRouteController) { - mCurrentRouteController = null; - } - } - - private void showMessageToast(String message) { - Toast toast = Toast.makeText(mCastContextApplicationContext, message, Toast.LENGTH_SHORT); - toast.show(); - } - - private void resetPlayingVideo() { - if (mNotificationControl != null) { - mNotificationControl.setRouteController(mCurrentRouteController); - } - } - - @VisibleForTesting - static RemoteMediaPlayerController getIfExists() { - return sInstance; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java deleted file mode 100644 index 3c1695be..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java +++ /dev/null
@@ -1,334 +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. - -package org.chromium.chrome.browser.media.remote; - -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.CastDevice; -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaStatus; -import com.google.android.gms.cast.RemoteMediaPlayer; -import com.google.android.gms.cast.RemoteMediaPlayer.MediaChannelResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.chrome.browser.media.router.CastSessionUtil; -import org.chromium.chrome.browser.media.router.FlingingController; -import org.chromium.chrome.browser.media.router.MediaController; -import org.chromium.chrome.browser.media.router.MediaStatusBridge; -import org.chromium.chrome.browser.media.router.MediaStatusObserver; -import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; -import org.chromium.chrome.browser.media.ui.MediaNotificationManager; - -import java.util.Locale; -import java.util.Random; - -/** - * A wrapper around a RemoteMediaPlayer that exposes simple playback commands without the - * the complexities of the GMS cast calls. - */ -public class RemoteMediaPlayerWrapper implements RemoteMediaPlayer.OnMetadataUpdatedListener, - RemoteMediaPlayer.OnStatusUpdatedListener, - ResultCallback<MediaChannelResult>, - MediaController, FlingingController { - private static final String TAG = "MediaRemoting"; - - private final CastDevice mCastDevice; - private final String mMediaUrl; - - private GoogleApiClient mApiClient; - private RemoteMediaPlayer mMediaPlayer; - private MediaNotificationInfo.Builder mNotificationBuilder; - private MediaStatusObserver mMediaStatusObserver; - - private Random mRequestIdGenerator = new Random(); - private long mMediaSessionId; - private boolean mPendingSeek; - private long mPendingSeekTime; - - private boolean mLoaded; - - public RemoteMediaPlayerWrapper(GoogleApiClient apiClient, - MediaNotificationInfo.Builder notificationBuilder, CastDevice castDevice, - String mediaUrl) { - mApiClient = apiClient; - mCastDevice = castDevice; - mMediaUrl = mediaUrl; - mLoaded = false; - mNotificationBuilder = notificationBuilder; - - mMediaPlayer = new RemoteMediaPlayer(); - mMediaPlayer.setOnStatusUpdatedListener(this); - mMediaPlayer.setOnMetadataUpdatedListener(this); - - updateNotificationMetadata(); - } - - private void updateNotificationMetadata() { - CastSessionUtil.setNotificationMetadata(mNotificationBuilder, mCastDevice, mMediaPlayer); - MediaNotificationManager.show(mNotificationBuilder.build()); - } - - private boolean canSendCommand() { - return mApiClient != null && mMediaPlayer != null && mApiClient.isConnected(); - } - - // RemoteMediaPlayer.OnStatusUpdatedListener implementation. - @Override - public void onStatusUpdated() { - MediaStatus mediaStatus = mMediaPlayer.getMediaStatus(); - if (mediaStatus == null) return; - - if (mMediaStatusObserver != null) { - mMediaStatusObserver.onMediaStatusUpdate(new MediaStatusBridge(mediaStatus)); - } - - int playerState = mediaStatus.getPlayerState(); - if (playerState == MediaStatus.PLAYER_STATE_PAUSED - || playerState == MediaStatus.PLAYER_STATE_PLAYING) { - mNotificationBuilder.setPaused(playerState != MediaStatus.PLAYER_STATE_PLAYING); - mNotificationBuilder.setActions( - MediaNotificationInfo.ACTION_STOP | MediaNotificationInfo.ACTION_PLAY_PAUSE); - } else { - mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP); - } - MediaNotificationManager.show(mNotificationBuilder.build()); - } - - // RemoteMediaPlayer.OnMetadataUpdatedListener implementation. - @Override - public void onMetadataUpdated() { - updateNotificationMetadata(); - } - - /** - * Opportunistically tries to update |mMediaSessionId|. - * TODO(https://crbug.com/918644): Remove this code when no longer needed. - */ - private void updateMediaSessionId(String message) { - try { - JSONObject jsonMessage = new JSONObject(message); - String messageType = jsonMessage.getString("type"); - - if ("MEDIA_STATUS".equals(messageType)) { - JSONArray statusArray = jsonMessage.getJSONArray("status"); - JSONObject status = statusArray.getJSONObject(0); - - mMediaSessionId = status.getLong("mediaSessionId"); - } - } catch (JSONException e) { - // Ignore the exception. We are only looking to opportunistically capture the - // mediaSessionId. - } - } - - /** - * Forwards the message to the underlying RemoteMediaPlayer. - */ - public void onMediaMessage(String message) { - if (mMediaPlayer == null) return; - - // Needed to send manual seek messages. - updateMediaSessionId(message); - - try { - mMediaPlayer.onMessageReceived(mCastDevice, CastSessionUtil.MEDIA_NAMESPACE, message); - } catch (IllegalStateException e) { - // GMS throws with "Result already set" when receiving responses from multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - /** - * Starts loading the media URL, from the given position. - */ - public void load(long startTime) { - if (!canSendCommand()) return; - - mLoaded = true; - - MediaInfo.Builder mediaInfoBuilder = - new MediaInfo.Builder(mMediaUrl).setContentType("*/*").setStreamType( - MediaInfo.STREAM_TYPE_BUFFERED); - - mMediaPlayer.load(mApiClient, mediaInfoBuilder.build(), /* autoplay */ true, startTime) - .setResultCallback(this); - } - - /** - * Starts playback. No-op if are not in a valid state. - * Doesn't verify the command's success/failure. - */ - @Override - public void play() { - if (!canSendCommand()) return; - - if (!mLoaded) { - load(/* startTime */ 0); - return; - } - - try { - mMediaPlayer.play(mApiClient).setResultCallback(this); - } catch (IllegalStateException e) { - // GMS throws with message "Result already set" when making multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - /** - * Pauses playback. No-op if are not in a valid state. - * Doesn't verify the command's success/failure. - */ - @Override - public void pause() { - if (!canSendCommand()) return; - - try { - mMediaPlayer.pause(mApiClient).setResultCallback(this); - } catch (IllegalStateException e) { - // GMS throws with message "Result already set" when making multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - /** - * Sets the mute state. Does not affect the stream volume. - * No-op if are not in a valid state. Doesn't verify the command's success/failure. - */ - @Override - public void setMute(boolean mute) { - if (!canSendCommand()) return; - - try { - mMediaPlayer.setStreamMute(mApiClient, mute).setResultCallback(this); - } catch (IllegalStateException e) { - // GMS throws with message "Result already set" when making multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - /** - * Sets the stream volume. Does not affect the mute state. - * No-op if are not in a valid state. Doesn't verify the command's success/failure. - */ - @Override - public void setVolume(double volume) { - if (!canSendCommand()) return; - - try { - mMediaPlayer.setStreamVolume(mApiClient, volume).setResultCallback(this); - } catch (IllegalStateException e) { - // GMS throws with message "Result already set" when making multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - private String createManualSeekMessage(long seekTimeMillis) { - // The request ID does not matter for RemotePlayback. - int requestId = mRequestIdGenerator.nextInt(10000); - String message = String.format(Locale.ROOT, - "{\"requestId\":%d,\"type\":\"SEEK\",\"mediaSessionId\":%d,\"currentTime\":%.3f}", - requestId, mMediaSessionId, (double) seekTimeMillis / 1000); - return message; - } - - // TODO(https://crbug.com/918644) Remove this code when it is no longer needed. - private void sendManualSeek(long position) { - mPendingSeekTime = position; - mPendingSeek = true; - - final String message = createManualSeekMessage(position); - Cast.CastApi.sendMessage(mApiClient, CastSessionUtil.MEDIA_NAMESPACE, message) - .setResultCallback(new ResultCallback<Status>() { - @Override - public void onResult(Status result) { - mPendingSeek = false; - - if (!result.isSuccess()) { - Log.e(TAG, "Error when sending manual seek. Status code: %d", - result.getStatusCode()); - } - } - }); - } - - /** - * Seeks to the given position (in milliseconds). - * No-op if are not in a valid state. Doesn't verify the command's success/failure. - */ - @Override - public void seek(long position) { - if (!canSendCommand()) return; - - if (!mLoaded) { - load(position); - return; - } - - // We send a hand crafted message to the receiver rather than using the RemoteMediaPlayer, - // due to a crash in GMS code if ever a request times out. See https://crbug.com/876247 and - // https://crbug.com/914072. - try { - sendManualSeek(position); - } catch (IllegalStateException e) { - // GMS throws with message "Result already set" when making multiple API calls - // in a short amount of time, before results can be read. See https://crbug.com/853923. - } - } - - /** - * Called when the session has stopped, and we should no longer send commands. - */ - public void clearApiClient() { - mApiClient = null; - } - - // ResultCallback<MediaChannelResult> implementation - @Override - public void onResult(MediaChannelResult result) { - // When multiple API calls are made in quick succession, "Results have already been set" - // IllegalStateExceptions might be thrown from GMS code. We prefer to catch the exception - // and noop it, than to crash. This might lead to some API calls never getting their - // onResult() called, so we should not rely on onResult() being called for every API call. - // See https://crbug.com/853923. - if (!result.getStatus().isSuccess()) { - Log.e(TAG, "Error when sending command. Status code: %d", - result.getStatus().getStatusCode()); - } - } - - // FlingingController implementation - @Override - public MediaController getMediaController() { - return this; - } - - @Override - public long getApproximateCurrentTime() { - // media::Pipeline's seek completes before we get a MediaStatus message with an - // updated timestamp. This gives the appearance of the media time jumping around right after - // a seek. Send |mPendingSeekTime| when seeking, to prevent media time from being clamped - // when seeking backwards. - return mPendingSeek ? mPendingSeekTime : mMediaPlayer.getApproximateStreamPosition(); - } - - @Override - public void setMediaStatusObserver(MediaStatusObserver observer) { - assert mMediaStatusObserver == null; - mMediaStatusObserver = observer; - } - - @Override - public void clearMediaStatusObserver() { - assert mMediaStatusObserver != null; - mMediaStatusObserver = null; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfo.java deleted file mode 100644 index f4030cf..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfo.java +++ /dev/null
@@ -1,113 +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. - -package org.chromium.chrome.browser.media.remote; - -import android.support.annotation.IntDef; -import android.text.TextUtils; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Exposes information about the current video to the external clients. - */ -public class RemoteVideoInfo { - /** - * This lists all the states that the remote video can be in. - */ - @IntDef({PlayerState.STOPPED, PlayerState.LOADING, PlayerState.PLAYING, PlayerState.PAUSED, - PlayerState.ERROR, PlayerState.INVALIDATED, PlayerState.FINISHED}) - @Retention(RetentionPolicy.SOURCE) - public @interface PlayerState { - /** The remote player is currently stopped. */ - int STOPPED = 0; - /** The remote player is buffering this video. */ - int LOADING = 1; - /** The remote player is playing this video. */ - int PLAYING = 2; - /** The remote player is paused. */ - int PAUSED = 3; - /** The remote player is in an error state. */ - int ERROR = 4; - /** The remote player has been replaced by another player (so the current session has - * finished) */ - int INVALIDATED = 5; - /** The remote video has completed playing. */ - int FINISHED = 6; - } - - /** - * The title of the video - */ - public String title; - /** - * The duration of the video - */ - public long durationMillis; - /** - * The current state of the video - */ - public @PlayerState int state; - /** - * The last known position in the video - */ - public long currentTimeMillis; - /** - * The current error message, if any - */ - // TODO(aberent) At present nothing sets this to anything other than Null. - public String errorMessage; - - /** - * Create a new RemoteVideoInfo - * @param title - * @param durationMillis - * @param state - * @param currentTimeMillis - * @param errorMessage - */ - public RemoteVideoInfo(String title, long durationMillis, @PlayerState int state, - long currentTimeMillis, String errorMessage) { - this.title = title; - this.durationMillis = durationMillis; - this.state = state; - this.currentTimeMillis = currentTimeMillis; - this.errorMessage = errorMessage; - } - - /** - * Copy a remote video info - * @param other the source. - */ - public RemoteVideoInfo(RemoteVideoInfo other) { - this(other.title, other.durationMillis, other.state, other.currentTimeMillis, - other.errorMessage); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (!(obj instanceof RemoteVideoInfo)) return false; - - RemoteVideoInfo other = (RemoteVideoInfo) obj; - return durationMillis == other.durationMillis - && currentTimeMillis == other.currentTimeMillis - && state == other.state - && TextUtils.equals(title, other.title) - && TextUtils.equals(errorMessage, other.errorMessage); - } - - @Override - public int hashCode() { - int result = (int) durationMillis; - result = 31 * result + (int) (durationMillis >> 32); - result = 31 * result + (int) currentTimeMillis; - result = 31 * result + (int) (currentTimeMillis >> 32); - result = 31 * result + (title == null ? 0 : title.hashCode()); - result = 31 * result + state; - result = 31 * result + (errorMessage == null ? 0 : errorMessage.hashCode()); - return result; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java index 57872ee3..e7ae381 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -18,11 +18,8 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.chrome.browser.AppHooks; -import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.media.router.caf.CafMediaRouteProvider; import org.chromium.chrome.browser.media.router.caf.remoting.CafRemotingMediaRouteProvider; -import org.chromium.chrome.browser.media.router.cast.CastMediaRouteProvider; -import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaRouteProvider; import java.util.ArrayList; import java.util.HashMap; @@ -43,33 +40,19 @@ new MediaRouteProvider.Factory() { @Override public void addProviders(MediaRouteManager manager) { - // Bypass feature check in tests as ChromeFeatureList might not be initialized - // yet. - if (ChromeFeatureList.isInitialized() - && ChromeFeatureList.isEnabled( - ChromeFeatureList.CAF_MEDIA_ROUTER_IMPL)) { - int googleApiAvailabilityResult = - AppHooks.get().isGoogleApiAvailableWithMinApkVersion( - MIN_GOOGLE_PLAY_SERVICES_APK_VERSION); - if (googleApiAvailabilityResult != ConnectionResult.SUCCESS) { - GoogleApiAvailability.getInstance().showErrorNotification( - ContextUtils.getApplicationContext(), - googleApiAvailabilityResult); - return; - } - MediaRouteProvider cafProvider = CafMediaRouteProvider.create(manager); - manager.addMediaRouteProvider(cafProvider); - MediaRouteProvider remotingProvider = - CafRemotingMediaRouteProvider.create(manager); - manager.addMediaRouteProvider(remotingProvider); - } else { - MediaRouteProvider castProvider = CastMediaRouteProvider.create(manager); - manager.addMediaRouteProvider(castProvider); - - MediaRouteProvider remotingProvider = - RemotingMediaRouteProvider.create(manager); - manager.addMediaRouteProvider(remotingProvider); + int googleApiAvailabilityResult = + AppHooks.get().isGoogleApiAvailableWithMinApkVersion( + MIN_GOOGLE_PLAY_SERVICES_APK_VERSION); + if (googleApiAvailabilityResult != ConnectionResult.SUCCESS) { + GoogleApiAvailability.getInstance().showErrorNotification( + ContextUtils.getApplicationContext(), googleApiAvailabilityResult); + return; } + MediaRouteProvider cafProvider = CafMediaRouteProvider.create(manager); + manager.addMediaRouteProvider(cafProvider); + MediaRouteProvider remotingProvider = + CafRemotingMediaRouteProvider.create(manager); + manager.addMediaRouteProvider(remotingProvider); } };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java index 61f9462..420a541 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java
@@ -10,8 +10,8 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; -import org.chromium.chrome.browser.media.router.cast.CastMediaSource; -import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaSource; +import org.chromium.chrome.browser.media.router.caf.CastMediaSource; +import org.chromium.chrome.browser.media.router.caf.remoting.RemotingMediaSource; /** * Implements the JNI interface called from the C++ Media Router dialog controller implementation
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java index 9c1d501..2801659 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java
@@ -21,7 +21,6 @@ import org.chromium.chrome.browser.media.router.MediaRouteProvider; import org.chromium.chrome.browser.media.router.MediaSink; import org.chromium.chrome.browser.media.router.MediaSource; -import org.chromium.chrome.browser.media.router.cast.CastMediaSource; import java.util.HashMap; import java.util.Map;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java index 2e8349e..e17f61a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java
@@ -25,7 +25,6 @@ import org.chromium.chrome.browser.media.router.CastSessionUtil; import org.chromium.chrome.browser.media.router.ClientRecord; import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.cast.CastMediaSource; import java.io.IOException; import java.util.ArrayDeque;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaSource.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CastMediaSource.java similarity index 98% rename from chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaSource.java rename to chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CastMediaSource.java index 2f3abda..3947f24 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaSource.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/CastMediaSource.java
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.media.router.cast; +package org.chromium.chrome.browser.media.router.caf; import android.net.Uri; import android.support.annotation.Nullable; @@ -18,7 +18,6 @@ /** * Abstracts parsing the Cast application id and other parameters from the source ID. */ -// Reused in CafMRP. No need to migrate. See https://crbug.com/711860. public class CastMediaSource implements MediaSource { public static final String AUTOJOIN_CUSTOM_CONTROLLER_SCOPED = "custom_controller_scoped"; public static final String AUTOJOIN_TAB_AND_ORIGIN_SCOPED = "tab_and_origin_scoped";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java index f45de12a..0954fd4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java
@@ -117,13 +117,10 @@ // requestWindowFeature must be called before adding content. setContentView(R.layout.expanded_cast_controller); - // mHandler = new Handler(); ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content); rootView.setBackgroundColor(Color.BLACK); - // mMediaRouteController.addUiListener(this); - // Create and initialize the media control UI. mMediaController = (MediaController) findViewById(R.id.cast_media_controller); mMediaController.setDelegate(mControllerDelegate);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java index 4ac2698..15b46d4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java
@@ -15,7 +15,6 @@ import org.chromium.chrome.browser.media.router.MediaSource; import org.chromium.chrome.browser.media.router.caf.BaseSessionController; import org.chromium.chrome.browser.media.router.caf.CafBaseMediaRouteProvider; -import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaSource; /** A {@link MediaRouteProvider} implementation for remoting, using Cast v3 API. */ public class CafRemotingMediaRouteProvider extends CafBaseMediaRouteProvider {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingMediaSource.java similarity index 98% rename from chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java rename to chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingMediaSource.java index 744add6..19a9d73 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingMediaSource.java
@@ -1,7 +1,7 @@ // Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -package org.chromium.chrome.browser.media.router.cast.remoting; +package org.chromium.chrome.browser.media.router.caf.remoting; import android.content.Context; import android.content.pm.ApplicationInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java index f7d181c..ec3d6d72 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java
@@ -11,7 +11,6 @@ import org.chromium.chrome.browser.media.router.caf.BaseNotificationController; import org.chromium.chrome.browser.media.router.caf.BaseSessionController; import org.chromium.chrome.browser.media.router.caf.CafBaseMediaRouteProvider; -import org.chromium.chrome.browser.media.router.cast.remoting.RemotingMediaSource; import java.lang.ref.WeakReference;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java deleted file mode 100644 index f4513a8..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java +++ /dev/null
@@ -1,257 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -package org.chromium.chrome.browser.media.router.cast; - -import android.os.Handler; -import android.support.annotation.Nullable; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; - -import org.chromium.base.Log; -import org.chromium.chrome.browser.media.router.DiscoveryCallback; -import org.chromium.chrome.browser.media.router.DiscoveryDelegate; -import org.chromium.chrome.browser.media.router.FlingingController; -import org.chromium.chrome.browser.media.router.MediaRoute; -import org.chromium.chrome.browser.media.router.MediaRouteManager; -import org.chromium.chrome.browser.media.router.MediaRouteProvider; -import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.MediaSource; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -/** - * A {@link BaseMediaRouteProvider} common implementation for MediaRouteProviders. - */ -public abstract class BaseMediaRouteProvider - implements MediaRouteProvider, DiscoveryDelegate, - ChromeCastSessionManager.CastSessionManagerListener { - private static final String TAG = "MediaRouter"; - - protected static final List<MediaSink> NO_SINKS = Collections.emptyList(); - - protected final MediaRouter mAndroidMediaRouter; - protected final MediaRouteManager mManager; - protected final Map<String, DiscoveryCallback> mDiscoveryCallbacks = - new HashMap<String, DiscoveryCallback>(); - protected final Map<String, MediaRoute> mRoutes = new HashMap<String, MediaRoute>(); - protected Handler mHandler = new Handler(); - - // There can be only one Cast session at the same time on Android. - protected CastSession mSession; - - protected BaseMediaRouteProvider(MediaRouter androidMediaRouter, MediaRouteManager manager) { - mAndroidMediaRouter = androidMediaRouter; - mManager = manager; - } - - /** - * @return A MediaSource object constructed from |sourceId|, or null if the derived class does - * not support the source. - */ - @Nullable - protected abstract MediaSource getSourceFromId(@Nonnull String sourceId); - - /** - * @return A CastSessionLaunchRequest encapsulating a session launch request. - */ - @Nullable - protected abstract ChromeCastSessionManager.CastSessionLaunchRequest createSessionLaunchRequest( - MediaSource source, MediaSink sink, String presentationId, String origin, int tabId, - boolean isIncognito, int nativeRequestId); - - /** - * Forward the sinks back to the native counterpart. - */ - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - protected void onSinksReceivedInternal(String sourceId, @Nonnull List<MediaSink> sinks) { - Log.d(TAG, "Reporting %d sinks for source: %s", sinks.size(), sourceId); - mManager.onSinksReceived(sourceId, this, sinks); - } - - /** - * {@link DiscoveryDelegate} implementation. - */ - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSinksReceived(String sourceId, @Nonnull List<MediaSink> sinks) { - Log.d(TAG, "Received %d sinks for sourceId: %s", sinks.size(), sourceId); - mHandler.post(() -> { onSinksReceivedInternal(sourceId, sinks); }); - } - - /** - * {@link MediaRouteProvider} implementation. - */ - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public boolean supportsSource(@Nonnull String sourceId) { - return getSourceFromId(sourceId) != null; - } - - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void startObservingMediaSinks(@Nonnull String sourceId) { - Log.d(TAG, "startObservingMediaSinks: " + sourceId); - - if (mAndroidMediaRouter == null) { - // If the MediaRouter API is not available, report no devices so the page doesn't even - // try to cast. - onSinksReceived(sourceId, NO_SINKS); - return; - } - - MediaSource source = getSourceFromId(sourceId); - if (source == null) { - // If the source is invalid or not supported by this provider, report no devices - // available. - onSinksReceived(sourceId, NO_SINKS); - return; - } - - // No-op, if already monitoring the application for this source. - String applicationId = source.getApplicationId(); - DiscoveryCallback callback = mDiscoveryCallbacks.get(applicationId); - if (callback != null) { - callback.addSourceUrn(sourceId); - return; - } - - MediaRouteSelector routeSelector = source.buildRouteSelector(); - if (routeSelector == null) { - // If the application invalid, report no devices available. - onSinksReceived(sourceId, NO_SINKS); - return; - } - - List<MediaSink> knownSinks = new ArrayList<MediaSink>(); - for (RouteInfo route : mAndroidMediaRouter.getRoutes()) { - if (route.matchesSelector(routeSelector)) { - knownSinks.add(MediaSink.fromRoute(route)); - } - } - - callback = new DiscoveryCallback(sourceId, knownSinks, this, routeSelector); - mAndroidMediaRouter.addCallback( - routeSelector, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); - mDiscoveryCallbacks.put(applicationId, callback); - } - - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void stopObservingMediaSinks(@Nonnull String sourceId) { - Log.d(TAG, "stopObservingMediaSinks: " + sourceId); - if (mAndroidMediaRouter == null) return; - - MediaSource source = getSourceFromId(sourceId); - if (source == null) return; - - String applicationId = source.getApplicationId(); - DiscoveryCallback callback = mDiscoveryCallbacks.get(applicationId); - if (callback == null) return; - - callback.removeSourceUrn(sourceId); - - if (callback.isEmpty()) { - mAndroidMediaRouter.removeCallback(callback); - mDiscoveryCallbacks.remove(applicationId); - } - } - - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void createRoute(String sourceId, String sinkId, String presentationId, String origin, - int tabId, boolean isIncognito, int nativeRequestId) { - if (mAndroidMediaRouter == null) { - mManager.onRouteRequestError("Not supported", nativeRequestId); - return; - } - - MediaRouter.RouteInfo targetRouteInfo = null; - for (MediaRouter.RouteInfo routeInfo : mAndroidMediaRouter.getRoutes()) { - if (routeInfo.getId().equals(sinkId)) { - targetRouteInfo = routeInfo; - break; - } - } - - if (targetRouteInfo == null) { - mManager.onRouteRequestError("No sink", nativeRequestId); - return; - } - - MediaSink sink = MediaSink.fromRoute(targetRouteInfo); - - MediaSource source = getSourceFromId(sourceId); - if (source == null) { - mManager.onRouteRequestError("Unsupported source URL", nativeRequestId); - return; - } - - // When the user clicks a route on the MediaRouteChooserDialog, we intercept the click event - // and do not select the route. Instead the route selection is postponed to here. This will - // make sure the MediaRouteControllerDialog show up properly. - targetRouteInfo.select(); - - ChromeCastSessionManager.CastSessionLaunchRequest request = createSessionLaunchRequest( - source, sink, presentationId, origin, tabId, isIncognito, nativeRequestId); - - ChromeCastSessionManager.get().requestSessionLaunch(request); - } - - @Override - public abstract void joinRoute( - String sourceId, String presentationId, String origin, int tabId, int nativeRequestId); - - @Override - public abstract void closeRoute(String routeId); - - @Override - public abstract void detachRoute(String routeId); - - @Override - public abstract void sendStringMessage(String routeId, String message); - - // ChromeCastSessionObserver implementation. - @Override - public abstract void onSessionStarting( - ChromeCastSessionManager.CastSessionLaunchRequest originalRequest); - - @Override - public abstract void onSessionEnded(); - - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionStartFailed() { - for (String routeId : mRoutes.keySet()) { - mManager.onRouteClosed(routeId, "Launch error"); - } - mRoutes.clear(); - }; - - // Migrated to CafBaseMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionStarted(CastSession session) { - mSession = session; - } - - // Migrated to CafBaseMediaRouteProvider.endAllRoutes(). See https://crbug.com/711860. - @Override - public void onSessionStopAction() { - if (mSession == null) return; - - for (String routeId : mRoutes.keySet()) closeRoute(routeId); - } - - @Override - @Nullable - public FlingingController getFlingingController(String routeId) { - return null; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java deleted file mode 100644 index 31cfdfb..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java +++ /dev/null
@@ -1,443 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.support.annotation.Nullable; -import android.support.v7.media.MediaRouter; - -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.media.router.ChromeMediaRouter; -import org.chromium.chrome.browser.media.router.ClientRecord; -import org.chromium.chrome.browser.media.router.MediaRoute; -import org.chromium.chrome.browser.media.router.MediaRouteManager; -import org.chromium.chrome.browser.media.router.MediaRouteProvider; -import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.MediaSource; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * A {@link MediaRouteProvider} implementation for Cast devices and applications. - */ -public class CastMediaRouteProvider extends BaseMediaRouteProvider { - private static final String TAG = "MediaRouter"; - - private static final String AUTO_JOIN_PRESENTATION_ID = "auto-join"; - private static final String PRESENTATION_ID_SESSION_ID_PREFIX = "cast-session_"; - - private final CastMessageHandler mMessageHandler; - private ClientRecord mLastRemovedRouteRecord; - private final Map<String, ClientRecord> mClientRecords = new HashMap<String, ClientRecord>(); - - /** - * @return Initialized {@link CastMediaRouteProvider} object. - */ - public static CastMediaRouteProvider create(MediaRouteManager manager) { - return new CastMediaRouteProvider(ChromeMediaRouter.getAndroidMediaRouter(), manager); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionStartFailed() { - super.onSessionStartFailed(); - mClientRecords.clear(); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionStarted(CastSession session) { - super.onSessionStarted(session); - mMessageHandler.onSessionCreated(mSession); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionEnded() { - if (mSession == null) return; - - if (mClientRecords.isEmpty()) { - for (String routeId : mRoutes.keySet()) mManager.onRouteTerminated(routeId); - mRoutes.clear(); - } else { - mLastRemovedRouteRecord = mClientRecords.values().iterator().next(); - for (ClientRecord client : mClientRecords.values()) { - mManager.onRouteTerminated(client.routeId); - - mRoutes.remove(client.routeId); - } - mClientRecords.clear(); - } - - mSession = null; - - if (mAndroidMediaRouter != null) { - mAndroidMediaRouter.selectRoute(mAndroidMediaRouter.getDefaultRoute()); - } - } - - public void onMessage(String clientId, String message) { - ClientRecord clientRecord = mClientRecords.get(clientId); - if (clientRecord == null) return; - - if (!clientRecord.isConnected) { - Log.d(TAG, "Queueing message to client %s: %s", clientId, message); - clientRecord.pendingMessages.add(message); - return; - } - - Log.d(TAG, "Sending message to client %s: %s", clientId, message); - mManager.onMessage(clientRecord.routeId, message); - } - - public CastMessageHandler getMessageHandler() { - return mMessageHandler; - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - public Set<String> getClients() { - return mClientRecords.keySet(); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - public Map<String, ClientRecord> getClientRecords() { - return mClientRecords; - } - - @Override - protected MediaSource getSourceFromId(String sourceId) { - return CastMediaSource.from(sourceId); - } - - @Override - protected ChromeCastSessionManager.CastSessionLaunchRequest createSessionLaunchRequest( - MediaSource source, MediaSink sink, String presentationId, String origin, int tabId, - boolean isIncognito, int nativeRequestId) { - return new CreateRouteRequest(source, sink, presentationId, origin, tabId, isIncognito, - nativeRequestId, this, CreateRouteRequest.RequestedCastSessionType.CAST, - mMessageHandler); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void onSessionStarting( - ChromeCastSessionManager.CastSessionLaunchRequest sessionLaunchRequest) { - CreateRouteRequest request = (CreateRouteRequest) sessionLaunchRequest; - MediaSink sink = request.getSink(); - MediaSource source = request.getSource(); - - MediaRoute route = - new MediaRoute(sink.getId(), source.getSourceId(), request.getPresentationId()); - addRoute(route, request.getOrigin(), request.getTabId()); - mManager.onRouteCreated(route.id, route.sinkId, request.getNativeRequestId(), this, true); - - String clientId = ((CastMediaSource) source).getClientId(); - - if (clientId != null) { - ClientRecord clientRecord = mClientRecords.get(clientId); - if (clientRecord != null) { - sendReceiverAction(clientRecord.routeId, sink, clientId, "cast"); - } - } - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void joinRoute(String sourceId, String presentationId, String origin, int tabId, - int nativeRequestId) { - CastMediaSource source = CastMediaSource.from(sourceId); - if (source == null || source.getClientId() == null) { - mManager.onRouteRequestError("Unsupported presentation URL", nativeRequestId); - return; - } - - if (mSession == null) { - mManager.onRouteRequestError("No presentation", nativeRequestId); - return; - } - - if (!canJoinExistingSession(presentationId, origin, tabId, source)) { - mManager.onRouteRequestError("No matching route", nativeRequestId); - return; - } - - MediaRoute route = new MediaRoute(mSession.getSinkId(), sourceId, presentationId); - addRoute(route, origin, tabId); - mManager.onRouteCreated(route.id, route.sinkId, nativeRequestId, this, false); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void closeRoute(String routeId) { - MediaRoute route = mRoutes.get(routeId); - if (route == null) return; - - if (mSession == null) { - mRoutes.remove(routeId); - mManager.onRouteTerminated(routeId); - return; - } - - ClientRecord client = getClientRecordByRouteId(routeId); - if (client != null && mAndroidMediaRouter != null) { - MediaSink sink = MediaSink.fromSinkId(mSession.getSinkId(), mAndroidMediaRouter); - if (sink != null) sendReceiverAction(routeId, sink, client.clientId, "stop"); - } - - ChromeCastSessionManager.get().stopApplication(); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Override - public void detachRoute(String routeId) { - mRoutes.remove(routeId); - - removeClient(getClientRecordByRouteId(routeId)); - } - - // Migrated to CafMessageHandler. See https://crbug.com/711860. - @Override - public void sendStringMessage(String routeId, String message) { - Log.d(TAG, "Received message from client: %s", message); - - if (!mRoutes.containsKey(routeId)) { - return; - } - - try { - JSONObject jsonMessage = new JSONObject(message); - - String messageType = jsonMessage.getString("type"); - // TODO(zqzhang): Move the handling of "client_connect", "client_disconnect" and - // "leave_session" from CastMRP to CastMessageHandler. Also, need to have a - // ClientManager for client managing. - if ("client_connect".equals(messageType)) { - handleClientConnectMessage(jsonMessage); - } else if ("client_disconnect".equals(messageType)) { - handleClientDisconnectMessage(jsonMessage); - } else if ("leave_session".equals(messageType)) { - handleLeaveSessionMessage(jsonMessage); - } else if (mSession != null) { - mMessageHandler.handleSessionMessage(jsonMessage); - } - } catch (JSONException e) { - Log.e(TAG, "JSONException while handling internal message: " + e); - } - } - - // Migrated to CafMessageHandler. See https://crbug.com/711860. - private boolean handleClientConnectMessage(JSONObject jsonMessage) throws JSONException { - String clientId = jsonMessage.getString("clientId"); - if (clientId == null) return false; - - ClientRecord clientRecord = mClientRecords.get(clientId); - if (clientRecord == null) return false; - - clientRecord.isConnected = true; - if (mSession != null) mSession.onClientConnected(clientId); - - if (clientRecord.pendingMessages.size() == 0) return true; - for (String message : clientRecord.pendingMessages) { - Log.d(TAG, "Deqeueing message for client %s: %s", clientId, message); - mManager.onMessage(clientRecord.routeId, message); - } - clientRecord.pendingMessages.clear(); - - return true; - } - - // Migrated to CafMessageHandler. See https://crbug.com/711860. - private boolean handleClientDisconnectMessage(JSONObject jsonMessage) throws JSONException { - String clientId = jsonMessage.getString("clientId"); - if (clientId == null) return false; - - ClientRecord client = mClientRecords.get(clientId); - if (client == null) return false; - - mRoutes.remove(client.routeId); - removeClient(client); - - mManager.onRouteTerminated(client.routeId); - - return true; - } - - // Migrated to CafMessageHandler. See https://crbug.com/711860. - private boolean handleLeaveSessionMessage(JSONObject jsonMessage) throws JSONException { - String clientId = jsonMessage.getString("clientId"); - if (clientId == null || mSession == null) return false; - - String sessionId = jsonMessage.getString("message"); - if (!mSession.getSessionId().equals(sessionId)) return false; - - ClientRecord leavingClient = mClientRecords.get(clientId); - if (leavingClient == null) return false; - - int sequenceNumber = jsonMessage.optInt("sequenceNumber", -1); - onMessage(clientId, buildInternalMessage("leave_session", sequenceNumber, clientId, null)); - - // Send a "disconnect_session" message to all the clients that match with the leaving - // client's auto join policy. - for (ClientRecord client : mClientRecords.values()) { - if ((CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy) - && isSameOrigin(client.origin, leavingClient.origin) - && client.tabId == leavingClient.tabId) - || (CastMediaSource.AUTOJOIN_ORIGIN_SCOPED.equals(leavingClient.autoJoinPolicy) - && isSameOrigin(client.origin, leavingClient.origin))) { - onMessage(client.clientId, - buildInternalMessage("disconnect_session", -1, client.clientId, sessionId)); - } - } - - return true; - } - - // Migrated to CafMessageHandler. See https://crbug.com/711860. - private String buildInternalMessage( - String type, int sequenceNumber, String clientId, String message) throws JSONException { - JSONObject jsonMessage = new JSONObject(); - jsonMessage.put("type", type); - jsonMessage.put("sequenceNumber", sequenceNumber); - jsonMessage.put("timeoutMillis", 0); - jsonMessage.put("clientId", clientId); - jsonMessage.put("message", message); - return jsonMessage.toString(); - } - - @VisibleForTesting - CastMediaRouteProvider(MediaRouter androidMediaRouter, MediaRouteManager manager) { - super(androidMediaRouter, manager); - mMessageHandler = new CastMessageHandler(this); - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - private boolean canAutoJoin(CastMediaSource source, String origin, int tabId) { - if (source.getAutoJoinPolicy().equals(CastMediaSource.AUTOJOIN_PAGE_SCOPED)) return false; - - CastMediaSource currentSource = CastMediaSource.from(mSession.getSourceId()); - if (!currentSource.getApplicationId().equals(source.getApplicationId())) return false; - - ClientRecord client = null; - if (!mClientRecords.isEmpty()) { - client = mClientRecords.values().iterator().next(); - } else if (mLastRemovedRouteRecord != null) { - client = mLastRemovedRouteRecord; - return isSameOrigin(origin, client.origin) && tabId == client.tabId; - } - if (client == null) return false; - - boolean sameOrigin = isSameOrigin(origin, client.origin); - if (source.getAutoJoinPolicy().equals(CastMediaSource.AUTOJOIN_ORIGIN_SCOPED)) { - return sameOrigin; - } else if (source.getAutoJoinPolicy().equals( - CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED)) { - return sameOrigin && tabId == client.tabId; - } - - return false; - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - private boolean canJoinExistingSession( - String presentationId, String origin, int tabId, CastMediaSource source) { - if (AUTO_JOIN_PRESENTATION_ID.equals(presentationId)) { - return canAutoJoin(source, origin, tabId); - } else if (presentationId.startsWith(PRESENTATION_ID_SESSION_ID_PREFIX)) { - String sessionId = presentationId.substring(PRESENTATION_ID_SESSION_ID_PREFIX.length()); - if (mSession.getSessionId().equals(sessionId)) return true; - } else { - for (MediaRoute route : mRoutes.values()) { - if (route.presentationId.equals(presentationId)) return true; - } - } - return false; - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @Nullable - private ClientRecord getClientRecordByRouteId(String routeId) { - for (ClientRecord record : mClientRecords.values()) { - if (record.routeId.equals(routeId)) return record; - } - return null; - } - - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - @VisibleForTesting - void addRoute(MediaRoute route, String origin, int tabId) { - mRoutes.put(route.id, route); - - CastMediaSource source = CastMediaSource.from(route.sourceId); - final String clientId = source.getClientId(); - - if (clientId == null || mClientRecords.get(clientId) != null) return; - - mClientRecords.put(clientId, - new ClientRecord( - route.id, - clientId, - source.getApplicationId(), - source.getAutoJoinPolicy(), - origin, - tabId)); - } - - // Migrated to CastMessageHandler.sendReceiverActionToClient. See https://crbug.com/711860. - private void sendReceiverAction( - String routeId, MediaSink sink, String clientId, String action) { - try { - JSONObject jsonReceiver = new JSONObject(); - jsonReceiver.put("label", sink.getId()); - jsonReceiver.put("friendlyName", sink.getName()); - jsonReceiver.put("capabilities", CastSessionImpl.getCapabilities(sink.getDevice())); - jsonReceiver.put("volume", null); - jsonReceiver.put("isActiveInput", null); - jsonReceiver.put("displayStatus", null); - jsonReceiver.put("receiverType", "cast"); - - JSONObject jsonReceiverAction = new JSONObject(); - jsonReceiverAction.put("receiver", jsonReceiver); - jsonReceiverAction.put("action", action); - - JSONObject json = new JSONObject(); - json.put("type", "receiver_action"); - json.put("sequenceNumber", -1); - json.put("timeoutMillis", 0); - json.put("clientId", clientId); - json.put("message", jsonReceiverAction); - - onMessage(clientId, json.toString()); - } catch (JSONException e) { - Log.e(TAG, "Failed to send receiver action message", e); - } - } - - private void removeClient(@Nullable ClientRecord client) { - if (client == null) return; - - mLastRemovedRouteRecord = client; - mClientRecords.remove(client.clientId); - } - - /** - * Compares two origins. Empty origin strings correspond to unique origins in - * url::Origin. - * - * @param originA A URL origin. - * @param originB A URL origin. - * @return True if originA and originB represent the same origin, false otherwise. - */ - // Migrated to CafMediaRouteProvider. See https://crbug.com/711860. - private static final boolean isSameOrigin(String originA, String originB) { - if (originA == null || originA.isEmpty() || originB == null || originB.isEmpty()) - return false; - return originA.equals(originB); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandler.java deleted file mode 100644 index 0e2ea67..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandler.java +++ /dev/null
@@ -1,627 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.os.Handler; -import android.support.v4.util.ArrayMap; -import android.util.SparseArray; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.media.router.CastRequestIdGenerator; -import org.chromium.chrome.browser.media.router.CastSessionUtil; -import org.chromium.chrome.browser.media.router.ClientRecord; - -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Queue; - -/** - * The handler for cast messages. It receives events between the Cast SDK and the page, process and - * dispatch the messages accordingly. The handler talks to the Cast SDK via CastSession, and - * talks to the pages via the media router. - */ -// Migrated to CafMessageHandler. See https://crbug.com/711860. -public class CastMessageHandler { - private static final String TAG = "MediaRouter"; - - // Sequence number used when no sequence number is required or was initially passed. - static final int INVALID_SEQUENCE_NUMBER = -1; - - private static final String MEDIA_MESSAGE_TYPES[] = { - "PLAY", - "LOAD", - "PAUSE", - "SEEK", - "STOP_MEDIA", - "MEDIA_SET_VOLUME", - "MEDIA_GET_STATUS", - "EDIT_TRACKS_INFO", - "QUEUE_LOAD", - "QUEUE_INSERT", - "QUEUE_UPDATE", - "QUEUE_REMOVE", - "QUEUE_REORDER", - }; - - private static final String MEDIA_SUPPORTED_COMMANDS[] = { - "pause", - "seek", - "stream_volume", - "stream_mute", - }; - - // Lock used to lazy initialize sMediaOverloadedMessageTypes. - private static final Object INIT_LOCK = new Object(); - - // Map associating types that have a different names outside of the media namespace and inside. - // In other words, some types are sent as MEDIA_FOO or FOO_MEDIA by the client by the Cast - // expect them to be named FOO. The reason being that FOO might exist in multiple namespaces - // but the client isn't aware of namespacing. - private static Map<String, String> sMediaOverloadedMessageTypes; - - private SparseArray<RequestRecord> mRequests; - private ArrayMap<String, Queue<Integer>> mStopRequests; - private Queue<RequestRecord> mVolumeRequests; - - // The reference to CastSession, only valid after calling {@link onSessionCreated}, and will be - // reset to null when calling {@link onApplicationStopped}. - private CastSession mSession; - private final CastMediaRouteProvider mRouteProvider; - private Handler mHandler; - - /** - * The record for client requests. {@link CastMessageHandler} uses this class to manage the - * client requests and match responses to the requests. - */ - static class RequestRecord { - public final String clientId; - public final int sequenceNumber; - - public RequestRecord(String clientId, int sequenceNumber) { - this.clientId = clientId; - this.sequenceNumber = sequenceNumber; - } - } - - /** - * Initializes a new {@link CastMessageHandler} instance. - * @param session The {@link CastSession} for communicating with the Cast SDK. - * @param provider The {@link CastMediaRouteProvider} for communicating with the page. - */ - public CastMessageHandler(CastMediaRouteProvider provider) { - mRouteProvider = provider; - mRequests = new SparseArray<RequestRecord>(); - mStopRequests = new ArrayMap<String, Queue<Integer>>(); - mVolumeRequests = new ArrayDeque<RequestRecord>(); - mHandler = new Handler(); - - synchronized (INIT_LOCK) { - if (sMediaOverloadedMessageTypes == null) { - sMediaOverloadedMessageTypes = new HashMap<String, String>(); - sMediaOverloadedMessageTypes.put("STOP_MEDIA", "STOP"); - sMediaOverloadedMessageTypes.put("MEDIA_SET_VOLUME", "SET_VOLUME"); - sMediaOverloadedMessageTypes.put("MEDIA_GET_STATUS", "GET_STATUS"); - } - } - } - - @VisibleForTesting - static String[] getMediaMessageTypesForTest() { - return MEDIA_MESSAGE_TYPES; - } - - @VisibleForTesting - static Map<String, String> getMediaOverloadedMessageTypesForTest() { - return sMediaOverloadedMessageTypes; - } - - @VisibleForTesting - SparseArray<RequestRecord> getRequestsForTest() { - return mRequests; - } - - @VisibleForTesting - Queue<RequestRecord> getVolumeRequestsForTest() { - return mVolumeRequests; - } - - @VisibleForTesting - Map<String, Queue<Integer>> getStopRequestsForTest() { - return mStopRequests; - } - - /** - * Set the session when a session is created, and notify all clients that are not connected. - * @param session The newly created session. - */ - public void onSessionCreated(CastSession session) { - mSession = session; - for (ClientRecord client : mRouteProvider.getClientRecords().values()) { - if (!client.isConnected) continue; - - mSession.onClientConnected(client.clientId); - } - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Functions for handling messages from the page to the Cast device. - - /** - * Handles messages related to the cast session, i.e. messages happening on a established - * connection. All these messages are sent from the page to the Cast SDK. - * @param message The JSONObject message to be handled. - */ - public boolean handleSessionMessage(JSONObject message) throws JSONException { - String messageType = message.getString("type"); - if ("v2_message".equals(messageType)) { - return handleCastV2Message(message); - } else if ("app_message".equals(messageType)) { - return handleAppMessage(message); - } else { - Log.e(TAG, "Unsupported message: %s", message); - return false; - } - } - - // An example of the Cast V2 message: - // { - // "type": "v2_message", - // "message": { - // "type": "...", - // ... - // }, - // "sequenceNumber": 0, - // "timeoutMillis": 0, - // "clientId": "144042901280235697" - // } - @VisibleForTesting - boolean handleCastV2Message(JSONObject jsonMessage) - throws JSONException { - assert "v2_message".equals(jsonMessage.getString("type")); - - final String clientId = jsonMessage.getString("clientId"); - if (clientId == null || !mRouteProvider.getClients().contains(clientId)) return false; - - JSONObject jsonCastMessage = jsonMessage.getJSONObject("message"); - String messageType = jsonCastMessage.getString("type"); - final int sequenceNumber = jsonMessage.optInt("sequenceNumber", INVALID_SEQUENCE_NUMBER); - - if ("STOP".equals(messageType)) { - handleStopMessage(clientId, sequenceNumber); - return true; - } - - if ("SET_VOLUME".equals(messageType)) { - CastSession.HandleVolumeMessageResult result = mSession.handleVolumeMessage( - jsonCastMessage.getJSONObject("volume"), clientId, sequenceNumber); - if (!result.mSucceeded) return false; - - // For each successful volume message we need to respond with an empty "v2_message" so - // the Cast Web SDK can call the success callback of the page. If we expect the volume - // to change as the result of the command, we're relying on {@link - // Cast.CastListener#onVolumeChanged} to get called by the Android Cast SDK when the - // receiver status is updated. We keep the sequence number until then. If the volume - // doesn't change as the result of the command, we won't get notified by the Android SDK - // when the status update is received so we respond to the volume message immediately. - if (result.mShouldWaitForVolumeChange) { - mVolumeRequests.add(new RequestRecord(clientId, sequenceNumber)); - } else { - // It's usually bad to have request and response on the same call stack so post the - // response to the Android message loop. - mHandler.post(new Runnable() { - @Override - public void run() { - onVolumeChanged(clientId, sequenceNumber); - } - }); - } - return true; - } - - if (Arrays.asList(MEDIA_MESSAGE_TYPES).contains(messageType)) { - if (sMediaOverloadedMessageTypes.containsKey(messageType)) { - messageType = sMediaOverloadedMessageTypes.get(messageType); - jsonCastMessage.put("type", messageType); - } - return sendJsonCastMessage( - jsonCastMessage, CastSessionUtil.MEDIA_NAMESPACE, clientId, sequenceNumber); - } - - return true; - } - - @VisibleForTesting - void handleStopMessage(String clientId, int sequenceNumber) { - Queue<Integer> sequenceNumbersForClient = mStopRequests.get(clientId); - if (sequenceNumbersForClient == null) { - sequenceNumbersForClient = new ArrayDeque<Integer>(); - mStopRequests.put(clientId, sequenceNumbersForClient); - } - sequenceNumbersForClient.add(sequenceNumber); - - mSession.stopApplication(); - } - - // An example of the Cast application message: - // { - // "type":"app_message", - // "message": { - // "sessionId":"...", - // "namespaceName":"...", - // "message": ... - // }, - // "sequenceNumber":0, - // "timeoutMillis":3000, - // "clientId":"14417311915272175" - // } - @VisibleForTesting - boolean handleAppMessage(JSONObject jsonMessage) throws JSONException { - assert "app_message".equals(jsonMessage.getString("type")); - - String clientId = jsonMessage.getString("clientId"); - if (clientId == null || !mRouteProvider.getClients().contains(clientId)) return false; - - JSONObject jsonAppMessageWrapper = jsonMessage.getJSONObject("message"); - - if (!mSession.getSessionId().equals(jsonAppMessageWrapper.getString("sessionId"))) { - return false; - } - - String namespaceName = jsonAppMessageWrapper.getString("namespaceName"); - if (namespaceName == null || namespaceName.isEmpty()) return false; - - if (!mSession.getNamespaces().contains(namespaceName)) return false; - - int sequenceNumber = jsonMessage.optInt("sequenceNumber", INVALID_SEQUENCE_NUMBER); - - Object actualMessageObject = jsonAppMessageWrapper.get("message"); - if (actualMessageObject == null) return false; - - if (actualMessageObject instanceof String) { - String actualMessage = jsonAppMessageWrapper.getString("message"); - return mSession.sendStringCastMessage( - actualMessage, namespaceName, clientId, sequenceNumber); - } - - JSONObject actualMessage = jsonAppMessageWrapper.getJSONObject("message"); - return sendJsonCastMessage(actualMessage, namespaceName, clientId, sequenceNumber); - } - - @VisibleForTesting - boolean sendJsonCastMessage( - JSONObject message, - final String namespace, - final String clientId, - final int sequenceNumber) throws JSONException { - if (mSession.isApiClientInvalid()) return false; - - removeNullFields(message); - - // Map the request id to a valid sequence number only. - if (sequenceNumber != INVALID_SEQUENCE_NUMBER) { - // If for some reason, there is already a requestId other than 0, it - // is kept. Otherwise, one is generated. In all cases it's associated with the - // sequenceNumber passed by the client. - int requestId = message.optInt("requestId", 0); - if (requestId == 0) { - requestId = CastRequestIdGenerator.getNextRequestId(); - message.put("requestId", requestId); - } - mRequests.append(requestId, new RequestRecord(clientId, sequenceNumber)); - } - - return mSession.sendStringCastMessage( - message.toString(), namespace, clientId, sequenceNumber); - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Functions for handling messages from the Cast device to the pages. - - /** - * Forwards the messages from the Cast device to the clients, and perform proper actions if it - * is media message. - * @param namespace The application specific namespace this message belongs to. - * @param message The message within the namespace that's being sent by the receiver - */ - public void onMessageReceived(String namespace, String message) { - RequestRecord request = null; - try { - JSONObject jsonMessage = new JSONObject(message); - int requestId = jsonMessage.getInt("requestId"); - if (mRequests.indexOfKey(requestId) >= 0) { - request = mRequests.get(requestId); - mRequests.delete(requestId); - } - } catch (JSONException e) { - } - - if (CastSessionUtil.MEDIA_NAMESPACE.equals(namespace)) { - onMediaMessage(message, request); - return; - } - - onAppMessage(message, namespace, request); - } - - /** - * Forwards the media message to the page via the media router. - * The MEDIA_STATUS message needs to be sent to all the clients. - * @param message The media that's being send by the receiver. - * @param request The information about the client and the sequence number to respond with. - */ - @VisibleForTesting - void onMediaMessage(String message, RequestRecord request) { - mSession.onMediaMessage(message); - - if (isMediaStatusMessage(message)) { - // MEDIA_STATUS needs to be sent to all the clients. - for (String clientId : mRouteProvider.getClients()) { - if (request != null && clientId.equals(request.clientId)) continue; - - sendClientMessageTo( - clientId, "v2_message", message, INVALID_SEQUENCE_NUMBER); - } - } - if (request != null) { - sendClientMessageTo( - request.clientId, "v2_message", message, request.sequenceNumber); - } - } - - /** - * Forwards the application specific message to the page via the media router. - * @param message The message within the namespace that's being sent by the receiver. - * @param namespace The application specific namespace this message belongs to. - * @param request The information about the client and the sequence number to respond with. - */ - @VisibleForTesting - void onAppMessage(String message, String namespace, RequestRecord request) { - try { - JSONObject jsonMessage = new JSONObject(); - jsonMessage.put("sessionId", mSession.getSessionId()); - jsonMessage.put("namespaceName", namespace); - jsonMessage.put("message", message); - if (request != null) { - sendClientMessageTo(request.clientId, "app_message", - jsonMessage.toString(), request.sequenceNumber); - } else { - broadcastClientMessage("app_message", jsonMessage.toString()); - } - } catch (JSONException e) { - Log.e(TAG, "Failed to create the message wrapper", e); - } - } - - /** - * Notifies the application has stopped to all requesting clients. - */ - public void onApplicationStopped() { - for (String clientId : mRouteProvider.getClients()) { - Queue<Integer> sequenceNumbersForClient = mStopRequests.get(clientId); - if (sequenceNumbersForClient == null) { - sendClientMessageTo( - clientId, "remove_session", mSession.getSessionId(), - INVALID_SEQUENCE_NUMBER); - continue; - } - - for (int sequenceNumber : sequenceNumbersForClient) { - sendClientMessageTo( - clientId, "remove_session", mSession.getSessionId(), sequenceNumber); - } - mStopRequests.remove(clientId); - } - mSession = null; - } - - /** - * When the Cast device volume really changed, updates the session status and notify all - * requesting clients. - */ - public void onVolumeChanged() { - mSession.updateSessionStatus(); - - if (mVolumeRequests.isEmpty()) return; - - for (RequestRecord r : mVolumeRequests) onVolumeChanged(r.clientId, r.sequenceNumber); - mVolumeRequests.clear(); - } - - @VisibleForTesting - void onVolumeChanged(String clientId, int sequenceNumber) { - sendClientMessageTo(clientId, "v2_message", null, sequenceNumber); - } - - /** - * Notifies a client that an app message has been sent. - * @param clientId The client id the message is sent from. - * @param sequenceNumber The sequence number of the message. - */ - public void onAppMessageSent(String clientId, int sequenceNumber) { - sendClientMessageTo(clientId, "app_message", null, sequenceNumber); - } - - /** - * Broadcasts the message to all clients. - * @param type The type of the message. - * @param message The message to broadcast. - */ - public void broadcastClientMessage(String type, String message) { - for (String clientId : mRouteProvider.getClients()) { - sendClientMessageTo(clientId, type, message, INVALID_SEQUENCE_NUMBER); - } - } - - /** - * Sends a message to a specific client. - * @param clientId The id of the receiving client. - * @param type The type of the message. - * @param message The message to be sent. - * @param sequenceNumber The sequence number for matching requesting and responding messages. - */ - public void sendClientMessageTo( - String clientId, String type, String message, int sequenceNumber) { - mRouteProvider.onMessage(clientId, - buildInternalMessage(type, message, clientId, sequenceNumber)); - } - - @VisibleForTesting - String buildInternalMessage( - String type, String message, String clientId, int sequenceNumber) { - JSONObject json = new JSONObject(); - try { - json.put("type", type); - json.put("sequenceNumber", sequenceNumber); - json.put("timeoutMillis", 0); - json.put("clientId", clientId); - - // TODO(mlamouri): we should have a more reliable way to handle string, null and Object - // messages. - if (message == null - || "remove_session".equals(type) - || "disconnect_session".equals(type)) { - json.put("message", message); - } else { - JSONObject jsonMessage = new JSONObject(message); - if ("v2_message".equals(type) - && "MEDIA_STATUS".equals(jsonMessage.getString("type"))) { - sanitizeMediaStatusMessage(jsonMessage); - } - json.put("message", jsonMessage); - } - } catch (JSONException e) { - Log.e(TAG, "Failed to build the reply: " + e); - } - - return json.toString(); - } - - /** - * @return A message containing the information of the {@link CastSession}. - */ - public String buildSessionMessage() { - if (mSession == null) return "{}"; - - CastSessionInfo sessionInfo = mSession.getSessionInfo(); - if (sessionInfo == null) return "{}"; - - try { - // "volume" is a part of "receiver" initialized below. - JSONObject jsonVolume = new JSONObject(); - jsonVolume.put("level", sessionInfo.receiver.volume.level); - jsonVolume.put("muted", sessionInfo.receiver.volume.muted); - - // "receiver" is a part of "message" initialized below. - JSONObject jsonReceiver = new JSONObject(); - jsonReceiver.put("label", sessionInfo.receiver.label); - jsonReceiver.put("friendlyName", sessionInfo.receiver.friendlyName); - jsonReceiver.put("capabilities", toJSONArray(sessionInfo.receiver.capabilities)); - jsonReceiver.put("volume", jsonVolume); - jsonReceiver.put("isActiveInput", sessionInfo.receiver.isActiveInput); - jsonReceiver.put("displayStatus", sessionInfo.receiver.displayStatus); - jsonReceiver.put("receiverType", sessionInfo.receiver.receiverType); - - JSONArray jsonNamespaces = new JSONArray(); - for (String namespace : sessionInfo.namespaces) { - JSONObject jsonNamespace = new JSONObject(); - jsonNamespace.put("name", namespace); - jsonNamespaces.put(jsonNamespace); - } - - JSONObject jsonMessage = new JSONObject(); - jsonMessage.put("sessionId", sessionInfo.sessionId); - jsonMessage.put("statusText", sessionInfo.statusText); - jsonMessage.put("receiver", jsonReceiver); - jsonMessage.put("namespaces", jsonNamespaces); - jsonMessage.put("media", toJSONArray(sessionInfo.media)); - jsonMessage.put("status", sessionInfo.status); - jsonMessage.put("transportId", sessionInfo.transportId); - jsonMessage.put("appId", sessionInfo.appId); - jsonMessage.put("displayName", sessionInfo.displayName); - - return jsonMessage.toString(); - } catch (JSONException e) { - Log.w(TAG, "Building session message failed", e); - return "{}"; - } - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Utility functions - - /** - * Modifies the received MediaStatus message to match the format expected by the client. - */ - private void sanitizeMediaStatusMessage(JSONObject object) throws JSONException { - object.put("sessionId", mSession.getSessionId()); - - JSONArray mediaStatus = object.getJSONArray("status"); - for (int i = 0; i < mediaStatus.length(); ++i) { - JSONObject status = mediaStatus.getJSONObject(i); - status.put("sessionId", mSession.getSessionId()); - if (!status.has("supportedMediaCommands")) continue; - - JSONArray commands = new JSONArray(); - int bitfieldCommands = status.getInt("supportedMediaCommands"); - for (int j = 0; j < 4; ++j) { - if ((bitfieldCommands & (1 << j)) != 0) { - commands.put(MEDIA_SUPPORTED_COMMANDS[j]); - } - } - - status.put("supportedMediaCommands", commands); // Removes current entry. - } - } - - /** - * Remove 'null' fields from a JSONObject. This method calls itself recursively until all the - * fields have been looked at. - * TODO(mlamouri): move to some util class? - */ - private static void removeNullFields(Object object) throws JSONException { - if (object instanceof JSONArray) { - JSONArray array = (JSONArray) object; - for (int i = 0; i < array.length(); ++i) removeNullFields(array.get(i)); - } else if (object instanceof JSONObject) { - JSONObject json = (JSONObject) object; - JSONArray names = json.names(); - if (names == null) return; - for (int i = 0; i < names.length(); ++i) { - String key = names.getString(i); - if (json.isNull(key)) { - json.remove(key); - } else { - removeNullFields(json.get(key)); - } - } - } - } - - @VisibleForTesting - boolean isMediaStatusMessage(String message) { - try { - JSONObject jsonMessage = new JSONObject(message); - return "MEDIA_STATUS".equals(jsonMessage.getString("type")); - } catch (JSONException e) { - return false; - } - } - - private JSONArray toJSONArray(List<String> from) throws JSONException { - JSONArray result = new JSONArray(); - for (String entry : from) { - result.put(entry); - } - return result; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java deleted file mode 100644 index d08f4df..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java +++ /dev/null
@@ -1,137 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.support.annotation.Nullable; - -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.chrome.browser.media.router.FlingingController; - -import java.util.Set; - -/** - * The interface for talking to the GMS core. We need to isolate this - * interface from its implementation so that we can mock it with out - * dependencies on the GMS core. Otherwise junit tests will fail on - * the bots since they don't support GMS core for junit tests. For - * example, see: https://crbug.com/588758 - */ -public interface CastSession { - /** - * The return type for {@link handleVolumeMessage}. - */ - static class HandleVolumeMessageResult { - public final boolean mSucceeded; - public final boolean mShouldWaitForVolumeChange; - - /** - * Initializes a {@link HandleVolumeMessageResult}. - */ - public HandleVolumeMessageResult(boolean succeeded, boolean shouldWaitForVolumeChange) { - mSucceeded = succeeded; - mShouldWaitForVolumeChange = shouldWaitForVolumeChange; - } - } - - /** - * @return If the gms core api client is invalid. - */ - boolean isApiClientInvalid(); - - /** - * @return The sink id of the CastSession. - */ - String getSinkId(); - - /** - * @return The source id of the CastSession. - */ - String getSourceId(); - - /** - * @return The id of the CastSession. - */ - String getSessionId(); - - /** - * @return The namespaces supported by the CastSession. - */ - Set<String> getNamespaces(); - - /** - * @return The message handler of the CastSession. - */ - CastMessageHandler getMessageHandler(); - - /** - * @return The session information. - */ - CastSessionInfo getSessionInfo(); - - /** - * Sends the string message to the Cast device through the Cast SDK. - * @param message The message to send. - * @param namespace The namespace of the message. - * @param clientId The id of the client sending the message. - * @param sequenceNumber The sequence number of the message, which is used for matching - * responses to requests. - */ - boolean sendStringCastMessage( - String message, String namespace, String clientId, int sequenceNumber); - - /** - * Handles SET_VOLUME messages, and sets the volume of the Cast device through the Cast SDK. - * @param volume A JSONObject containing the volume information. - * Example: - * { - * "volume" { - * "level": 0.9, - * "muted": null - * } - * } - * @param clientId The id of the client sending the message. - * @param sequenceNumber The sequence number of the message, which is used for matching - * responses to requests. - */ - HandleVolumeMessageResult handleVolumeMessage( - JSONObject volume, String clientId, int sequenceNumber) - throws JSONException; - - /** - * Stops the application. The methods tells the Cast SDK to stop the application and on - * response, it will notify all the clients through the message handler. - */ - void stopApplication(); - - /** - * Perform proper actions when a client is connected to the session. - */ - void onClientConnected(String clientId); - - /** - * When a media message is received from the Cast device, forwards the message to the - * MediaPlayer. - * @param message The media message received from the Cast device. - */ - void onMediaMessage(String message); - - /** - * Perform proper actions when the Cast device volume has changed. - */ - void onVolumeChanged(); - - /** - * Updates the session info when it changes and broadcast the change. - */ - void updateSessionStatus(); - - /** - * Returns a controller for the media content. - */ - @Nullable - FlingingController getFlingingController(); -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java deleted file mode 100644 index 481069d8..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java +++ /dev/null
@@ -1,493 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.content.Intent; -import android.support.annotation.Nullable; - -import com.google.android.gms.cast.ApplicationMetadata; -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.CastDevice; -import com.google.android.gms.cast.MediaStatus; -import com.google.android.gms.cast.RemoteMediaPlayer; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.media.router.CastSessionUtil; -import org.chromium.chrome.browser.media.router.FlingingController; -import org.chromium.chrome.browser.media.router.MediaSource; -import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; -import org.chromium.chrome.browser.media.ui.MediaNotificationListener; -import org.chromium.chrome.browser.media.ui.MediaNotificationManager; -import org.chromium.chrome.browser.metrics.MediaNotificationUma; -import org.chromium.chrome.browser.tab.Tab; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * A wrapper around the established Cast application session. - */ -public class CastSessionImpl implements MediaNotificationListener, CastSession { - private static final String TAG = "MediaRouter"; - - private static class CastMessagingChannel implements Cast.MessageReceivedCallback { - private final CastSession mSession; - - public CastMessagingChannel(CastSessionImpl session) { - mSession = session; - } - - @Override - public void onMessageReceived(CastDevice castDevice, String namespace, String message) { - Log.d(TAG, "Received message from Cast device: namespace=\"" + namespace - + "\" message=\"" + message + "\""); - mSession.getMessageHandler().onMessageReceived(namespace, message); - } - } - - private final CastMessagingChannel mMessageChannel; - private final CastDevice mCastDevice; - private final MediaSource mSource; - private final CastMessageHandler mMessageHandler; - - private GoogleApiClient mApiClient; - private String mSessionId; - private String mApplicationStatus; - private ApplicationMetadata mApplicationMetadata; - private boolean mStoppingApplication; - private MediaNotificationInfo.Builder mNotificationBuilder; - private RemoteMediaPlayer mMediaPlayer; - - private Set<String> mNamespaces = new HashSet<String>(); - - /** - * Initializes a new {@link CastSessionImpl} instance. - * @param apiClient The Google Play Services client used to create the session. - * @param sessionId The session identifier to use with the Cast SDK. - * @param origin The origin of the frame requesting the route. - * @param tabId The id of the tab containing the frame requesting the route. - * @param isIncognito Whether the route is beging requested from an Incognito profile. - * @param source The {@link MediaSource} corresponding to this session. - * @param routeProvider The {@link CastMediaRouteProvider} instance managing this session. - */ - public CastSessionImpl(GoogleApiClient apiClient, String sessionId, - ApplicationMetadata metadata, String applicationStatus, CastDevice castDevice, - String origin, int tabId, boolean isIncognito, MediaSource source, - CastMessageHandler messageHandler) { - mSessionId = sessionId; - mApiClient = apiClient; - mSource = source; - mApplicationMetadata = metadata; - mApplicationStatus = applicationStatus; - mCastDevice = castDevice; - mMessageHandler = messageHandler; - mMessageChannel = new CastMessagingChannel(this); - updateNamespaces(); - - if (mNamespaces.contains(CastSessionUtil.MEDIA_NAMESPACE)) { - mMediaPlayer = new RemoteMediaPlayer(); - mMediaPlayer.setOnStatusUpdatedListener( - new RemoteMediaPlayer.OnStatusUpdatedListener() { - @Override - public void onStatusUpdated() { - MediaStatus mediaStatus = mMediaPlayer.getMediaStatus(); - if (mediaStatus == null) return; - - int playerState = mediaStatus.getPlayerState(); - if (playerState == MediaStatus.PLAYER_STATE_PAUSED - || playerState == MediaStatus.PLAYER_STATE_PLAYING) { - mNotificationBuilder.setPaused( - playerState != MediaStatus.PLAYER_STATE_PLAYING); - mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP - | MediaNotificationInfo.ACTION_PLAY_PAUSE); - } else { - mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP); - } - MediaNotificationManager.show(mNotificationBuilder.build()); - } - }); - mMediaPlayer.setOnMetadataUpdatedListener( - new RemoteMediaPlayer.OnMetadataUpdatedListener() { - @Override - public void onMetadataUpdated() { - CastSessionUtil.setNotificationMetadata( - mNotificationBuilder, mCastDevice, mMediaPlayer); - MediaNotificationManager.show(mNotificationBuilder.build()); - } - }); - } - - Intent contentIntent = Tab.createBringTabToFrontIntent(tabId); - if (contentIntent != null) { - contentIntent.putExtra(MediaNotificationUma.INTENT_EXTRA_NAME, - MediaNotificationUma.Source.PRESENTATION); - } - mNotificationBuilder = - new MediaNotificationInfo.Builder() - .setPaused(false) - .setOrigin(origin) - // TODO(avayvod): the same session might have more than one tab id. Should - // we track the last foreground alive tab and update the notification with - // it? - .setTabId(tabId) - .setPrivate(isIncognito) - .setActions(MediaNotificationInfo.ACTION_STOP) - .setContentIntent(contentIntent) - .setNotificationSmallIcon(R.drawable.ic_notification_media_route) - .setDefaultNotificationLargeIcon(R.drawable.cast_playing_square) - .setId(R.id.presentation_notification) - .setListener(this); - - CastSessionUtil.setNotificationMetadata(mNotificationBuilder, mCastDevice, mMediaPlayer); - MediaNotificationManager.show(mNotificationBuilder.build()); - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // MediaNotificationListener implementation. - - @Override - public void onPlay(int actionSource) { - if (mMediaPlayer == null || isApiClientInvalid()) return; - - mMediaPlayer.play(mApiClient); - } - - @Override - public void onPause(int actionSource) { - if (mMediaPlayer == null || isApiClientInvalid()) return; - - mMediaPlayer.pause(mApiClient); - } - - @Override - public void onStop(int actionSource) { - stopApplication(); - ChromeCastSessionManager.get().onSessionStopAction(); - } - - @Override - public void onMediaSessionAction(int action) {} - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Utility functions. - - /** - * @param device The {@link CastDevice} queried for it's capabilities. - * @return The capabilities of the Cast device. - * TODO(zqzhang): move to a CastUtils class? - */ - protected static List<String> getCapabilities(CastDevice device) { - List<String> capabilities = new ArrayList<String>(); - if (device.hasCapability(CastDevice.CAPABILITY_AUDIO_IN)) { - capabilities.add("audio_in"); - } - if (device.hasCapability(CastDevice.CAPABILITY_AUDIO_OUT)) { - capabilities.add("audio_out"); - } - if (device.hasCapability(CastDevice.CAPABILITY_VIDEO_IN)) { - capabilities.add("video_in"); - } - if (device.hasCapability(CastDevice.CAPABILITY_VIDEO_OUT)) { - capabilities.add("video_out"); - } - return capabilities; - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Namespace handling. - - private void updateNamespaces() { - if (mApplicationMetadata == null) return; - - List<String> newNamespaces = mApplicationMetadata.getSupportedNamespaces(); - - Set<String> toRemove = new HashSet<String>(mNamespaces); - toRemove.removeAll(newNamespaces); - for (String namespaceToRemove : toRemove) unregisterNamespace(namespaceToRemove); - - for (String newNamespace : newNamespaces) { - if (!mNamespaces.contains(newNamespace)) addNamespace(newNamespace); - } - } - - private void addNamespace(String namespace) { - assert !mNamespaces.contains(namespace); - - if (isApiClientInvalid()) return; - - // If application metadata is null, register the callback anyway. - if (mApplicationMetadata != null && !mApplicationMetadata.isNamespaceSupported(namespace)) { - return; - } - - try { - Cast.CastApi.setMessageReceivedCallbacks(mApiClient, namespace, mMessageChannel); - mNamespaces.add(namespace); - } catch (IOException e) { - Log.e(TAG, "Failed to register namespace listener for %s", namespace, e); - } - } - - private void unregisterNamespace(String namespace) { - assert mNamespaces.contains(namespace); - - if (isApiClientInvalid()) return; - - try { - Cast.CastApi.removeMessageReceivedCallbacks(mApiClient, namespace); - mNamespaces.remove(namespace); - } catch (IOException e) { - Log.e(TAG, "Failed to remove the namespace listener for %s", namespace, e); - } - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // CastSession implementations. - - @Override - public boolean isApiClientInvalid() { - return mApiClient == null || !mApiClient.isConnected(); - } - - @Override - public String getSourceId() { - return mSource.getSourceId(); - } - - @Override - public String getSinkId() { - return mCastDevice.getDeviceId(); - } - - @Override - public String getSessionId() { - return mSessionId; - } - - @Override - public Set<String> getNamespaces() { - return mNamespaces; - } - - @Override - public CastMessageHandler getMessageHandler() { - return mMessageHandler; - } - - @Override - public CastSessionInfo getSessionInfo() { - if (isApiClientInvalid()) return null; - - // Due to a Google Play Services issue, the api client might still get disconnected so that - // calls like {@link Cast.CastApi#getVolume()} will throw an {@link IllegalStateException}. - // Until the issue is fixed, catch the exception and return null if it was thrown instead of - // crashing. See https://crbug.com/708964 for details. - try { - CastSessionInfo.VolumeInfo.Builder volumeBuilder = - new CastSessionInfo.VolumeInfo.Builder() - .setLevel(Cast.CastApi.getVolume(mApiClient)) - .setMuted(Cast.CastApi.isMute(mApiClient)); - - CastSessionInfo.ReceiverInfo.Builder receiverBuilder = - new CastSessionInfo.ReceiverInfo.Builder() - .setLabel(mCastDevice.getDeviceId()) - .setFriendlyName(mCastDevice.getFriendlyName()) - .setVolume(volumeBuilder.build()) - .setIsActiveInput(Cast.CastApi.getActiveInputState(mApiClient)) - .setDisplayStatus(null) - .setReceiverType("cast") - .addCapabilities(getCapabilities(mCastDevice)); - - CastSessionInfo.Builder sessionInfoBuilder = - new CastSessionInfo.Builder() - .setSessionId(mSessionId) - .setStatusText(mApplicationStatus) - .setReceiver(receiverBuilder.build()) - .setStatus("connected") - .setTransportId("web-4") - .addNamespaces(mNamespaces); - - if (mApplicationMetadata != null) { - sessionInfoBuilder.setAppId(mApplicationMetadata.getApplicationId()) - .setDisplayName(mApplicationMetadata.getName()); - } else { - sessionInfoBuilder.setAppId(mSource.getApplicationId()) - .setDisplayName(mCastDevice.getFriendlyName()); - } - - return sessionInfoBuilder.build(); - } catch (IllegalStateException e) { - Log.e(TAG, "Couldn't get session info", e); - return null; - } - } - - @Override - public boolean sendStringCastMessage( - final String message, - final String namespace, - final String clientId, - final int sequenceNumber) { - if (isApiClientInvalid()) return false; - Log.d(TAG, "Sending message to Cast device in namespace %s: %s", namespace, message); - - try { - Cast.CastApi.sendMessage(mApiClient, namespace, message) - .setResultCallback( - new ResultCallback<Status>() { - @Override - public void onResult(Status result) { - if (!result.isSuccess()) { - // TODO(avayvod): should actually report back to the page. - // See https://crbug.com/550445. - Log.e(TAG, "Failed to send the message: " + result); - return; - } - - // Media commands wait for the media status update as a result. - if (CastSessionUtil.MEDIA_NAMESPACE.equals(namespace)) return; - - // App messages wait for the empty message with the sequence - // number. - mMessageHandler.onAppMessageSent(clientId, sequenceNumber); - } - }); - } catch (Exception e) { - Log.e(TAG, "Exception while sending message", e); - return false; - } - return true; - } - - // SET_VOLUME messages have a |level| and |muted| properties. One of them is - // |null| and the other one isn't. |muted| is expected to be a boolean while - // |level| is a float from 0.0 to 1.0. - // Example: - // { - // "volume" { - // "level": 0.9, - // "muted": null - // } - // } - @Override - public HandleVolumeMessageResult handleVolumeMessage( - JSONObject volume, final String clientId, final int sequenceNumber) - throws JSONException { - if (volume == null) return new HandleVolumeMessageResult(false, false); - - if (isApiClientInvalid()) return new HandleVolumeMessageResult(false, false); - - boolean waitForVolumeChange = false; - try { - if (!volume.isNull("muted")) { - boolean newMuted = volume.getBoolean("muted"); - if (Cast.CastApi.isMute(mApiClient) != newMuted) { - Cast.CastApi.setMute(mApiClient, newMuted); - waitForVolumeChange = true; - } - } - if (!volume.isNull("level")) { - double newLevel = volume.getDouble("level"); - double currentLevel = Cast.CastApi.getVolume(mApiClient); - if (!Double.isNaN(currentLevel) - && Math.abs(currentLevel - newLevel) - > CastSessionUtil.MIN_VOLUME_LEVEL_DELTA) { - Cast.CastApi.setVolume(mApiClient, newLevel); - waitForVolumeChange = true; - } - } - } catch (IOException e) { - Log.e(TAG, "Failed to send volume command: " + e); - return new HandleVolumeMessageResult(false, false); - } - - return new HandleVolumeMessageResult(true, waitForVolumeChange); - } - - @Override - public void stopApplication() { - if (mStoppingApplication) return; - - if (isApiClientInvalid()) return; - - mStoppingApplication = true; - Cast.CastApi.stopApplication(mApiClient, mSessionId) - .setResultCallback(new ResultCallback<Status>() { - @Override - public void onResult(Status status) { - mMessageHandler.onApplicationStopped(); - // TODO(avayvod): handle a failure to stop the application. - // https://crbug.com/535577 - - Set<String> namespaces = new HashSet<String>(mNamespaces); - for (String namespace : namespaces) unregisterNamespace(namespace); - mNamespaces.clear(); - - mSessionId = null; - mApiClient = null; - - ChromeCastSessionManager.get().onSessionEnded(); - mStoppingApplication = false; - - MediaNotificationManager.clear(R.id.presentation_notification); - } - }); - } - - @Override - public void onMediaMessage(String message) { - if (mMediaPlayer != null) { - mMediaPlayer.onMessageReceived(mCastDevice, CastSessionUtil.MEDIA_NAMESPACE, message); - } - } - - @Override - public void onVolumeChanged() { - mMessageHandler.onVolumeChanged(); - } - - @Override - public void updateSessionStatus() { - if (isApiClientInvalid()) return; - - try { - mApplicationStatus = Cast.CastApi.getApplicationStatus(mApiClient); - mApplicationMetadata = Cast.CastApi.getApplicationMetadata(mApiClient); - - updateNamespaces(); - - mMessageHandler.broadcastClientMessage( - "update_session", mMessageHandler.buildSessionMessage()); - } catch (IllegalStateException e) { - Log.e(TAG, "Can't get application status", e); - } - } - - @Override - public void onClientConnected(String clientId) { - mMessageHandler.sendClientMessageTo( - clientId, "new_session", mMessageHandler.buildSessionMessage(), - CastMessageHandler.INVALID_SEQUENCE_NUMBER); - - if (mMediaPlayer != null && !isApiClientInvalid()) mMediaPlayer.requestStatus(mApiClient); - } - - @Override - @Nullable - public FlingingController getFlingingController() { - // FlingingController is not used with the CastSessionImpl. - return null; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionInfo.java deleted file mode 100644 index 3ef11cf..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastSessionInfo.java +++ /dev/null
@@ -1,324 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * The metadata information of {@link CastSession}. - */ -public class CastSessionInfo { - /** - * The volume information of receivers. - */ - public static class VolumeInfo { - /** - * The receiver volume. - */ - public final double level; - /** - * Whether the receiver is muted. - */ - public final boolean muted; - - /** - * Use this class to construct an instance of {@link VolumeInfo}. - */ - public static class Builder { - private double mLevel; - private boolean mMuted; - - public Builder setLevel(double level) { - mLevel = level; - return this; - } - - public Builder setMuted(boolean muted) { - mMuted = muted; - return this; - } - - /** - * Initializes the builder with the default values. - */ - public Builder() { - } - - public VolumeInfo build() { - return new VolumeInfo(mLevel, mMuted); - } - } - - private VolumeInfo(double level, boolean muted) { - this.level = level; - this.muted = muted; - } - } - - /** - * The receiver information. - */ - public static class ReceiverInfo { - /** - * Label of the receiver. - */ - public final String label; - /** - * The friendly name of the receiver. - */ - public final String friendlyName; - /** - * Receiver capabilities. - */ - public final List<String> capabilities; - /** - * Receiver volume information. - */ - public final VolumeInfo volume; - /** - * The receiver device's active-input state. - */ - public final int isActiveInput; - /** - * The display status of the receiver. - */ - public final String displayStatus; - /** - * The type of the receiver. - */ - public final String receiverType; - - /** - * Use this class to create an instance of {@link ReceiverInfo}. - */ - public static class Builder { - private String mLabel = ""; - private String mFriendlyName = ""; - private List<String> mCapabilities = new ArrayList<String>(); - private VolumeInfo mVolume; - private int mIsActiveInput; - private String mDisplayStatus = ""; - private String mReceiverType = ""; - - public Builder setLabel(String label) { - mLabel = label; - return this; - } - - public Builder setFriendlyName(String friendlyName) { - mFriendlyName = friendlyName; - return this; - } - - public Builder addCapability(String capability) { - mCapabilities.add(capability); - return this; - } - - public Builder addCapabilities(Collection<String> capabilities) { - mCapabilities.addAll(capabilities); - return this; - } - - public Builder setVolume(VolumeInfo volume) { - mVolume = volume; - return this; - } - - public Builder setIsActiveInput(int isActiveInput) { - mIsActiveInput = isActiveInput; - return this; - } - - public Builder setDisplayStatus(String displayStatus) { - mDisplayStatus = displayStatus; - return this; - } - - public Builder setReceiverType(String receiverType) { - mReceiverType = receiverType; - return this; - } - - /** - * Initializes the builder with the default values. - */ - public Builder() { - } - - public ReceiverInfo build() { - return new ReceiverInfo( - mLabel, - mFriendlyName, - mCapabilities, - mVolume, - mIsActiveInput, - mDisplayStatus, - mReceiverType); - } - } - - private ReceiverInfo( - String label, - String friendlyName, - List<String> capabilities, - VolumeInfo volume, - int isActiveInput, - String displayStatus, - String receiverType) { - this.label = label; - this.friendlyName = friendlyName; - this.capabilities = capabilities; - this.volume = volume; - this.isActiveInput = isActiveInput; - this.displayStatus = displayStatus; - this.receiverType = receiverType; - } - } - - /** - * The id of the {@link CastSession}. - */ - public final String sessionId; - /** - * The application status (in Cast SDK) of the {@link CastSession}. - */ - public final String statusText; - /** - * The receiver information of the {@link CastSession}. - */ - public final ReceiverInfo receiver; - /** - * The namespaces registered in the {@link CastSession}. - */ - public final List<String> namespaces; - /** - * The media in the {@link CastSession}. - */ - public final List<String> media; - /** - * The status of the {@link CastSession}. - */ - public final String status; - /** - * The tranport id of the {@link CastSession}. - */ - public final String transportId; - /** - * The app id of the {@link CastSession}. - */ - public final String appId; - /** - * The display name of the {@link CastSession}. - */ - public final String displayName; - - /** - * Use this class to create an instance of {@link CastSessionInfo}. - */ - public static class Builder { - private String mSessionId = ""; - private String mStatusText = ""; - private ReceiverInfo mReceiver; - private List<String> mNamespaces = new ArrayList<String>(); - private List<String> mMedia = new ArrayList<String>(); - private String mStatus = ""; - private String mTransportId = ""; - private String mAppId = ""; - private String mDisplayName = ""; - - public Builder setSessionId(String sessionId) { - mSessionId = sessionId; - return this; - } - - public Builder setStatusText(String statusText) { - mStatusText = statusText; - return this; - } - - public Builder setReceiver(ReceiverInfo receiver) { - mReceiver = receiver; - return this; - } - - public Builder addNamespace(String namespace) { - mNamespaces.add(namespace); - return this; - } - - public Builder addNamespaces(Collection<String> namespaces) { - mNamespaces.addAll(namespaces); - return this; - } - - public Builder addMedia(String namespace) { - mMedia.add(namespace); - return this; - } - - public Builder setStatus(String status) { - mStatus = status; - return this; - } - - public Builder setTransportId(String transportId) { - mTransportId = transportId; - return this; - } - - public Builder setAppId(String appId) { - mAppId = appId; - return this; - } - - public Builder setDisplayName(String displayName) { - mDisplayName = displayName; - return this; - } - - /** - * Initializes the builder with the default values. - */ - public Builder() { - } - - public CastSessionInfo build() { - return new CastSessionInfo( - mSessionId, - mStatusText, - mReceiver, - mNamespaces, - mMedia, - mStatus, - mTransportId, - mAppId, - mDisplayName); - } - } - - private CastSessionInfo( - String sessionId, - String statusText, - ReceiverInfo receiver, - List<String> namespaces, - List<String> media, - String status, - String transportId, - String appId, - String displayName) { - this.sessionId = sessionId; - this.statusText = statusText; - this.receiver = receiver; - this.namespaces = namespaces; - this.media = media; - this.status = status; - this.transportId = transportId; - this.appId = appId; - this.displayName = displayName; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManager.java deleted file mode 100644 index 8131a74..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManager.java +++ /dev/null
@@ -1,231 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.annotation.SuppressLint; - -import com.google.android.gms.cast.ApplicationMetadata; -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.CastStatusCodes; - -import org.chromium.base.Log; - -/** - * Manages the lifetime of the active CastSessions and broadcasts state changes to observers. - */ -public class ChromeCastSessionManager { - private static final String TAG = "cr_CastSessionMgr"; - - // Singleton instance. - private static ChromeCastSessionManager sInstance; - - /** - * Defines the events relevant to CastSession state changes. - * NOTE: This interface is modeled after Cast SDK V3's SessionManagerListener, but does not map - * exactly to it. - */ - public interface CastSessionManagerListener { - // Called right before a session launch is started. - public void onSessionStarting( - ChromeCastSessionManager.CastSessionLaunchRequest originalRequest); - - // Called after a successful session launch. - public void onSessionStarted(CastSession session); - - // Called after a failed session launch. - public void onSessionStartFailed(); - - // Called when a session is shutting down (as a result of user action, or when a new - // session is being launched). - public void onSessionStopAction(); - - // Called after a session has shut down. - public void onSessionEnded(); - } - - /** - * Encapsulates a request to start a cast session. Mostly used to simplify testing. - */ - public interface CastSessionLaunchRequest { - // Starts the request. - // Success or failure will be reported to the ChromeCastSessionManager via the - // onSessionStarted() and onSessionStartFailed() calls. - public void start(Cast.Listener listener); - - // Gets the session observer that should be notified of the session launch. - public CastSessionManagerListener getSessionListener(); - } - - private class CastListener extends Cast.Listener { - private CastSession mSession; - - CastListener() {} - - void setSession(CastSession session) { - mSession = session; - } - - @Override - public void onApplicationStatusChanged() { - if (mSession == null) return; - - mSession.updateSessionStatus(); - } - - @Override - public void onApplicationMetadataChanged(ApplicationMetadata metadata) { - if (mSession == null) return; - - mSession.updateSessionStatus(); - } - - // TODO(https://crbug.com/635567): Fix this properly. - @Override - @SuppressLint("DefaultLocale") - public void onApplicationDisconnected(int errorCode) { - if (errorCode != CastStatusCodes.SUCCESS) { - Log.e(TAG, String.format("Application disconnected with: %d", errorCode)); - } - - // This callback can be called more than once if the application is stopped from Chrome. - if (mSession == null) return; - - mSession.stopApplication(); - mSession = null; - } - - @Override - public void onVolumeChanged() { - if (mSession == null) return; - - mSession.onVolumeChanged(); - } - } - - // The current active session. There can only be one active session on Android. - // NOTE: This is a Chromium abstraction, different from the Cast SDK CastSession. - private CastSession mSession; - - // Listener tied to |mSession|. - private CastListener mListener; - - // Request to be started once |mSession| is stopped. - private CastSessionLaunchRequest mPendingSessionLaunchRequest; - - // Object that initiated the current cast session and that should be notified - // of changes to the session. - private CastSessionManagerListener mCurrentSessionListener; - - // Whether we are currently in the process of launching a session. - private boolean mSessionLaunching; - - public static ChromeCastSessionManager get() { - if (sInstance == null) sInstance = new ChromeCastSessionManager(); - - return sInstance; - } - - /** - * Should only be used for testing purposes. - */ - public static void resetInstanceForTesting() { - sInstance = null; - } - - private ChromeCastSessionManager() {} - - /** - * Called after a session successfully launched and was created. - * @param session the newly created session. - */ - public void onSessionStarted(CastSession session) { - assert mSession == null; - - mSession = session; - mSessionLaunching = false; - mCurrentSessionListener.onSessionStarted(session); - mListener.setSession(session); - } - - /** - * Called to initiate a new session launch. Will stop the current session if it exists. - * Calls back into onSessionStarting(). - * Calling this method when we already have a session will stop that session first. - * Requests will be dropped if we are in the process of launching a new session. - * @param request the encapsulated information required to launch the session. - */ - public void requestSessionLaunch(CastSessionLaunchRequest request) { - // Do not attempt to launch more than one session at a time. - if (mSessionLaunching) return; - - // Since we only can only have one session, close it before starting a new one. - if (mSession != null) { - mPendingSessionLaunchRequest = request; - mSession.stopApplication(); - return; - } - - launchSession(request); - } - - /** - * Stops the current session. - */ - public void stopApplication() { - if (mSession != null) mSession.stopApplication(); - } - - /** - * Starts the session request. - * Will result in a call to onSessionStarted() or onSessionStartFailed() based on the - * success/failure of the launch. - */ - private void launchSession(CastSessionLaunchRequest request) { - assert mSession == null; - - mSessionLaunching = true; - - mCurrentSessionListener = request.getSessionListener(); - mCurrentSessionListener.onSessionStarting(request); - - mListener = new CastListener(); - - request.start(mListener); - } - - /** - * Called when the session has received a stop action. - */ - public void onSessionStopAction() { - mCurrentSessionListener.onSessionStopAction(); - } - - /** - * Called when the session has ended. The session may no longer be valid at this point. - */ - public void onSessionEnded() { - mCurrentSessionListener.onSessionEnded(); - - mSession = null; - mCurrentSessionListener = null; - mListener = null; - - if (mPendingSessionLaunchRequest != null) { - launchSession(mPendingSessionLaunchRequest); - mPendingSessionLaunchRequest = null; - } - } - - /** - * Called when the session has failed to launch. - */ - public void onSessionStartFailed() { - mCurrentSessionListener.onSessionStartFailed(); - mCurrentSessionListener = null; - mListener = null; - - mSessionLaunching = false; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java deleted file mode 100644 index 3f00e9b43..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java +++ /dev/null
@@ -1,286 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; - -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.LaunchOptions; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.PendingResult; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.chrome.browser.media.router.ChromeMediaRouter; -import org.chromium.chrome.browser.media.router.MediaRoute; -import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.MediaSource; -import org.chromium.chrome.browser.media.router.cast.remoting.RemotingCastSession; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Establishes a {@link MediaRoute} by starting a Cast application represented by the given - * presentation URL. Reports success or failure to {@link ChromeMediaRouter}. - * Since there're numerous asynchronous calls involved in getting the application to launch - * the class is implemented as a state machine. - */ -// Migrated to CafMediaRouteProvider. See https://crbug.com/711860. -public class CreateRouteRequest implements GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, - ResultCallback<Cast.ApplicationConnectionResult>, - ChromeCastSessionManager.CastSessionLaunchRequest { - private static final String TAG = "MediaRouter"; - - @IntDef({State.IDLE, State.CONNECTING_TO_API, State.API_CONNECTION_SUSPENDED, - State.LAUNCHING_APPLICATION, State.LAUNCH_SUCCEEDED, State.TERMINATED}) - @Retention(RetentionPolicy.SOURCE) - private @interface State { - int IDLE = 0; - int CONNECTING_TO_API = 1; - int API_CONNECTION_SUSPENDED = 2; - int LAUNCHING_APPLICATION = 3; - int LAUNCH_SUCCEEDED = 4; - int TERMINATED = 5; - } - - private final MediaSource mSource; - private final MediaSink mSink; - private final String mPresentationId; - private final String mOrigin; - private final int mTabId; - private final boolean mIsIncognito; - private final int mRequestId; - private final CastMessageHandler mMessageHandler; - private final ChromeCastSessionManager.CastSessionManagerListener mSessionListener; - private final @RequestedCastSessionType int mSessionType; - - private GoogleApiClient mApiClient; - private @State int mState = State.IDLE; - - // Used to identify whether the request should launch a CastSessionImpl or a RemotingCastSession - // (based off of wheter the route creation was requested by a RemotingMediaRouteProvider or a - // CastMediaRouteProvider). - @IntDef({RequestedCastSessionType.CAST, RequestedCastSessionType.REMOTE}) - @Retention(RetentionPolicy.SOURCE) - public @interface RequestedCastSessionType { - int CAST = 0; - int REMOTE = 1; - } - - /** - * Initializes the request. - * @param source The {@link MediaSource} defining the application to launch on the Cast device. - * @param sink The {@link MediaSink} identifying the selected Cast device. - * @param presentationId The presentation id assigned to the route by {@link ChromeMediaRouter}. - * @param origin The origin of the frame requesting the route. - * @param tabId The id of the tab containing the frame requesting the route. - * @param isIncognito Whether the route is being requested from an Incognito profile. - * @param requestId The id of the route creation request for tracking by - * {@link ChromeMediaRouter}. - * @param listener The listener that should be notified of session/route creation changes. - * @param messageHandler A message handler (used to create CastSessionImpl instances). - */ - public CreateRouteRequest(MediaSource source, MediaSink sink, String presentationId, - String origin, int tabId, boolean isIncognito, int requestId, - ChromeCastSessionManager.CastSessionManagerListener listener, - @RequestedCastSessionType int sessionType, - @Nullable CastMessageHandler messageHandler) { - assert source != null; - assert sink != null; - - mSource = source; - mSink = sink; - mPresentationId = presentationId; - mOrigin = origin; - mTabId = tabId; - mIsIncognito = isIncognito; - mRequestId = requestId; - mSessionListener = listener; - mSessionType = sessionType; - mMessageHandler = messageHandler; - } - - public MediaSource getSource() { - return mSource; - } - - public MediaSink getSink() { - return mSink; - } - - public String getPresentationId() { - return mPresentationId; - } - - public String getOrigin() { - return mOrigin; - } - - public int getTabId() { - return mTabId; - } - - public boolean isIncognito() { - return mIsIncognito; - } - - public int getNativeRequestId() { - return mRequestId; - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // ChromeCastSessionManager.CastSessionLaunchRequest implementation. - - /** - * Returns the object that should be notified of session changes that result - * from this route creation request. - */ - @Override - public ChromeCastSessionManager.CastSessionManagerListener getSessionListener() { - return mSessionListener; - } - - /** - * Starts the process of launching the application on the Cast device. - */ - @Override - public void start(Cast.Listener castListener) { - if (mState != State.IDLE) throwInvalidState(); - - mApiClient = createApiClient(castListener); - mApiClient.connect(); - mState = State.CONNECTING_TO_API; - } - - ///////////////////////////////////////////////////////////////////////////////////////////// - // GoogleApiClient.* implementations. - - @Override - public void onConnected(Bundle connectionHint) { - if (mState != State.CONNECTING_TO_API && mState != State.API_CONNECTION_SUSPENDED) { - throwInvalidState(); - } - - if (mState == State.API_CONNECTION_SUSPENDED) return; - - try { - launchApplication(mApiClient, mSource.getApplicationId(), true) - .setResultCallback(this); - mState = State.LAUNCHING_APPLICATION; - } catch (Exception e) { - Log.e(TAG, "Launch application failed: %s", mSource.getApplicationId(), e); - reportError(); - } - } - - @Override - public void onConnectionSuspended(int cause) { - mState = State.API_CONNECTION_SUSPENDED; - } - - @Override - public void onConnectionFailed(ConnectionResult result) { - if (mState != State.CONNECTING_TO_API) throwInvalidState(); - - Log.e(TAG, "GoogleApiClient connection failed: %d, %b", result.getErrorCode(), - result.hasResolution()); - reportError(); - } - - /** - * ResultCallback<Cast.ApplicationConnectionResult> implementation. - */ - @Override - public void onResult(Cast.ApplicationConnectionResult result) { - if (mState != State.LAUNCHING_APPLICATION && mState != State.API_CONNECTION_SUSPENDED) { - throwInvalidState(); - } - - Status status = result.getStatus(); - if (!status.isSuccess()) { - Log.e(TAG, "Launch application failed with status: %s, %d, %s", - mSource.getApplicationId(), status.getStatusCode(), status.getStatusMessage()); - reportError(); - return; - } - - mState = State.LAUNCH_SUCCEEDED; - reportSuccess(result); - } - - private GoogleApiClient createApiClient(Cast.Listener listener) { - Cast.CastOptions.Builder apiOptionsBuilder = - new Cast.CastOptions.Builder(mSink.getDevice(), listener) - // TODO(avayvod): hide this behind the flag or remove - .setVerboseLoggingEnabled(true); - - return new GoogleApiClient.Builder(ContextUtils.getApplicationContext()) - .addApi(Cast.API, apiOptionsBuilder.build()) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - } - - private PendingResult<Cast.ApplicationConnectionResult> launchApplication( - GoogleApiClient apiClient, - String appId, - boolean relaunchIfRunning) { - LaunchOptions.Builder builder = new LaunchOptions.Builder(); - return Cast.CastApi.launchApplication(apiClient, appId, - builder.setRelaunchIfRunning(relaunchIfRunning) - .build()); - } - - // TODO(crbug.com/635567): Fix this properly. - @SuppressLint("DefaultLocale") - private void throwInvalidState() { - throw new RuntimeException(String.format("Invalid state: %d", mState)); - } - - private void reportSuccess(Cast.ApplicationConnectionResult result) { - if (mState != State.LAUNCH_SUCCEEDED) throwInvalidState(); - - CastSession session = null; - - switch (mSessionType) { - case RequestedCastSessionType.CAST: - session = new CastSessionImpl(mApiClient, result.getSessionId(), - result.getApplicationMetadata(), result.getApplicationStatus(), - mSink.getDevice(), mOrigin, mTabId, mIsIncognito, mSource, mMessageHandler); - break; - case RequestedCastSessionType.REMOTE: - session = new RemotingCastSession(mApiClient, result.getSessionId(), - result.getApplicationMetadata(), result.getApplicationStatus(), - mSink.getDevice(), mOrigin, mTabId, mIsIncognito, mSource); - break; - } - - ChromeCastSessionManager.get().onSessionStarted(session); - - terminate(); - } - - private void reportError() { - if (mState == State.TERMINATED) throwInvalidState(); - - ChromeCastSessionManager.get().onSessionStartFailed(); - - terminate(); - } - - private void terminate() { - mApiClient.unregisterConnectionCallbacks(this); - mApiClient.unregisterConnectionFailedListener(this); - mState = State.TERMINATED; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java deleted file mode 100644 index 69c64a2d..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java +++ /dev/null
@@ -1,219 +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. - -package org.chromium.chrome.browser.media.router.cast.remoting; - -import com.google.android.gms.cast.ApplicationMetadata; -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.CastDevice; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.media.remote.RemoteMediaPlayerWrapper; -import org.chromium.chrome.browser.media.router.CastSessionUtil; -import org.chromium.chrome.browser.media.router.FlingingController; -import org.chromium.chrome.browser.media.router.MediaSource; -import org.chromium.chrome.browser.media.router.cast.CastMessageHandler; -import org.chromium.chrome.browser.media.router.cast.CastSession; -import org.chromium.chrome.browser.media.router.cast.CastSessionInfo; -import org.chromium.chrome.browser.media.router.cast.ChromeCastSessionManager; -import org.chromium.chrome.browser.media.ui.MediaNotificationInfo; -import org.chromium.chrome.browser.media.ui.MediaNotificationListener; -import org.chromium.chrome.browser.media.ui.MediaNotificationManager; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; - -/** - * A wrapper around a RemoteMediaPlayer, used in remote playback. - */ -// Migrated to RemotingSessionController. See https://crbug.com/711860. -public class RemotingCastSession - implements MediaNotificationListener, CastSession, Cast.MessageReceivedCallback { - private static final String TAG = "MediaRouter"; - - private final CastDevice mCastDevice; - private final MediaSource mSource; - - private GoogleApiClient mApiClient; - private String mSessionId; - private String mApplicationStatus; - private ApplicationMetadata mApplicationMetadata; - private MediaNotificationInfo.Builder mNotificationBuilder; - private RemoteMediaPlayerWrapper mMediaPlayerWrapper; - private boolean mStoppingApplication; - - public RemotingCastSession(GoogleApiClient apiClient, String sessionId, - ApplicationMetadata metadata, String applicationStatus, CastDevice castDevice, - String origin, int tabId, boolean isIncognito, MediaSource source) { - mCastDevice = castDevice; - mSource = source; - mApiClient = apiClient; - mApplicationMetadata = metadata; - mSessionId = sessionId; - - mNotificationBuilder = - new MediaNotificationInfo.Builder() - .setPaused(false) - .setOrigin(origin) - // TODO(avayvod): the same session might have more than one tab id. Should - // we track the last foreground alive tab and update the notification with - // it? - .setTabId(tabId) - .setPrivate(isIncognito) - .setActions(MediaNotificationInfo.ACTION_STOP) - .setNotificationSmallIcon(R.drawable.ic_notification_media_route) - .setDefaultNotificationLargeIcon(R.drawable.cast_playing_square) - .setId(R.id.presentation_notification) - .setListener(this); - - try { - Cast.CastApi.setMessageReceivedCallbacks( - mApiClient, CastSessionUtil.MEDIA_NAMESPACE, this); - } catch (IOException e) { - Log.e(TAG, "Failed to register media namespace listener", e); - } - - mMediaPlayerWrapper = new RemoteMediaPlayerWrapper(mApiClient, mNotificationBuilder, - mCastDevice, ((RemotingMediaSource) source).getMediaUrl()); - } - - /** - * Cast.MessageReceivedCallback implementation. - */ - @Override - public void onMessageReceived(CastDevice castDevice, String namespace, String message) { - if (!CastSessionUtil.MEDIA_NAMESPACE.equals(namespace)) { - Log.d(TAG, - "RemotingCastSession received non-media message from Cast device: namespace=\"" - + namespace + "\" message=\"" + message + "\""); - return; - } - - onMediaMessage(message); - } - - @Override - public boolean isApiClientInvalid() { - return mApiClient == null || !mApiClient.isConnected(); - } - - @Override - public String getSourceId() { - return mSource.getSourceId(); - } - - @Override - public String getSinkId() { - return mCastDevice.getDeviceId(); - } - - @Override - public String getSessionId() { - return mSessionId; - } - - @Override - public Set<String> getNamespaces() { - return new HashSet<String>(); - } - - @Override - public CastMessageHandler getMessageHandler() { - return null; - } - - @Override - public CastSessionInfo getSessionInfo() { - // Only used by the CastMessageHandler, which is unused in the RemotingCastSession case. - return null; - } - - @Override - public boolean sendStringCastMessage( - String message, String namespace, String clientId, int sequenceNumber) { - // String messages are not used in remoting scenarios. - return false; - } - - @Override - public HandleVolumeMessageResult handleVolumeMessage( - JSONObject volume, String clientId, int sequenceNumber) throws JSONException { - // RemoteMediaPlayer's setStreamVolume() should be used instead of volume messages. - return null; - } - - @Override - public void stopApplication() { - if (mStoppingApplication) return; - - if (isApiClientInvalid()) return; - - mStoppingApplication = true; - Cast.CastApi.stopApplication(mApiClient, mSessionId) - .setResultCallback(new ResultCallback<Status>() { - @Override - public void onResult(Status status) { - // TODO(https://crbug.com/535577): handle a failure to stop the application. - - mSessionId = null; - mApiClient = null; - mMediaPlayerWrapper.clearApiClient(); - - ChromeCastSessionManager.get().onSessionEnded(); - mStoppingApplication = false; - - MediaNotificationManager.clear(R.id.presentation_notification); - } - }); - } - - @Override - public void onClientConnected(String clientId) {} - - @Override - public void onMediaMessage(String message) { - mMediaPlayerWrapper.onMediaMessage(message); - } - - @Override - public void onVolumeChanged() {} - - @Override - public void updateSessionStatus() {} - - ///////////////////////////////////////////////////////////////////////////////////////////// - // MediaNotificationListener implementation. - - @Override - public void onPlay(int actionSource) { - mMediaPlayerWrapper.play(); - } - - @Override - public void onPause(int actionSource) { - mMediaPlayerWrapper.pause(); - } - - @Override - public void onStop(int actionSource) { - stopApplication(); - ChromeCastSessionManager.get().onSessionStopAction(); - } - - @Override - public void onMediaSessionAction(int action) {} - - @Override - public FlingingController getFlingingController() { - return mMediaPlayerWrapper; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java deleted file mode 100644 index 1ef9cb8..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java +++ /dev/null
@@ -1,160 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -package org.chromium.chrome.browser.media.router.cast.remoting; - -import android.support.annotation.Nullable; -import android.support.v7.media.MediaRouter; - -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.chrome.browser.media.router.ChromeMediaRouter; -import org.chromium.chrome.browser.media.router.FlingingController; -import org.chromium.chrome.browser.media.router.MediaRoute; -import org.chromium.chrome.browser.media.router.MediaRouteManager; -import org.chromium.chrome.browser.media.router.MediaRouteProvider; -import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.MediaSource; -import org.chromium.chrome.browser.media.router.cast.BaseMediaRouteProvider; -import org.chromium.chrome.browser.media.router.cast.CastSession; -import org.chromium.chrome.browser.media.router.cast.ChromeCastSessionManager; -import org.chromium.chrome.browser.media.router.cast.CreateRouteRequest; - -/** - * A {@link MediaRouteProvider} implementation for media remote playback. - */ -// Migrated to CafRemotingMediaRouteProvider. See https://crbug.com/711860. -public class RemotingMediaRouteProvider extends BaseMediaRouteProvider { - private static final String TAG = "MediaRemoting"; - - private int mPendingNativeRequestId; - private MediaRoute mPendingMediaRoute; - - /** - * @return Initialized {@link RemotingMediaRouteProvider} object. - */ - public static RemotingMediaRouteProvider create(MediaRouteManager manager) { - return new RemotingMediaRouteProvider(ChromeMediaRouter.getAndroidMediaRouter(), manager); - } - - @Override - protected MediaSource getSourceFromId(String sourceId) { - return RemotingMediaSource.from(sourceId); - } - - @Override - protected ChromeCastSessionManager.CastSessionLaunchRequest createSessionLaunchRequest( - MediaSource source, MediaSink sink, String presentationId, String origin, int tabId, - boolean isIncognito, int nativeRequestId) { - return new CreateRouteRequest(source, sink, presentationId, origin, tabId, isIncognito, - nativeRequestId, this, CreateRouteRequest.RequestedCastSessionType.REMOTE, null); - } - - @Override - public void joinRoute( - String sourceId, String presentationId, String origin, int tabId, int nativeRequestId) { - mManager.onRouteRequestError( - "Remote playback doesn't support joining routes", nativeRequestId); - } - - @Override - public void closeRoute(String routeId) { - MediaRoute route = mRoutes.get(routeId); - if (route == null) return; - - if (mSession == null) { - mRoutes.remove(routeId); - mManager.onRouteTerminated(routeId); - return; - } - - ChromeCastSessionManager.get().stopApplication(); - } - - @Override - public void detachRoute(String routeId) { - mRoutes.remove(routeId); - } - - @Override - public void sendStringMessage(String routeId, String message) { - Log.e(TAG, "Remote playback does not support sending messages"); - } - - @VisibleForTesting - RemotingMediaRouteProvider(MediaRouter androidMediaRouter, MediaRouteManager manager) { - super(androidMediaRouter, manager); - } - - @Override - public void onSessionEnded() { - if (mSession == null) return; - - for (String routeId : mRoutes.keySet()) mManager.onRouteTerminated(routeId); - mRoutes.clear(); - - mSession = null; - - if (mAndroidMediaRouter != null) { - mAndroidMediaRouter.selectRoute(mAndroidMediaRouter.getDefaultRoute()); - } - } - - @Override - public void onSessionStarting(ChromeCastSessionManager.CastSessionLaunchRequest launchRequest) { - CreateRouteRequest request = (CreateRouteRequest) launchRequest; - MediaSink sink = request.getSink(); - MediaSource source = request.getSource(); - - // Calling mManager.onRouteCreated() too early causes some issues. If we call it here - // directly, getMediaController() might be called before onSessionStarted(), which causes - // the FlingingRenderer's creation to fail. Instead, save the route and request ID, and only - // signal the route as having been created when onSessionStarted() is called. - mPendingMediaRoute = - new MediaRoute(sink.getId(), source.getSourceId(), request.getPresentationId()); - mPendingNativeRequestId = request.getNativeRequestId(); - } - - private void clearPendingRoute() { - mPendingMediaRoute = null; - mPendingNativeRequestId = 0; - } - - @Override - public void onSessionStarted(CastSession session) { - super.onSessionStarted(session); - - // Continued from onSessionStarting() - mRoutes.put(mPendingMediaRoute.id, mPendingMediaRoute); - mManager.onRouteCreated(mPendingMediaRoute.id, mPendingMediaRoute.sinkId, - mPendingNativeRequestId, this, true); - - clearPendingRoute(); - } - - @Override - public void onSessionStartFailed() { - super.onSessionStartFailed(); - - mManager.onRouteRequestError( - "Failure to start RemotingCastSession", mPendingNativeRequestId); - - clearPendingRoute(); - }; - - @Override - @Nullable - public FlingingController getFlingingController(String routeId) { - // We cannot return a FlingingController if we don't have a session. - if (mSession == null) return null; - - // Don't return controllers for stale routes. - if (!mRoutes.containsKey(routeId)) return null; - - // RemotePlayback does not support joining routes, which means we only - // have a single route active at a time. If we have a a valid CastSession - // and the route ID is current, this means that the given |mSession| - // corresponds to the route ID, and it is ok to return the FlingingController. - return mSession.getFlingingController(); - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java index 46491df..6cd045f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -80,7 +80,7 @@ } /** - * Records the fact that the merchant called CanMakePayment and records it's return value. + * Records the fact that the merchant called CanMakePayment and records its return value. * * @param value The return value of the CanMakePayment call. */ @@ -89,6 +89,15 @@ } /** + * Records the fact that the merchant called HasEnrolledInstrument and records its return value. + * + * @param value The return value of the HasEnrolledInstrument call. + */ + public void setHasEnrolledInstrumentValue(boolean value) { + nativeSetHasEnrolledInstrumentValue(mJourneyLoggerAndroid, value); + } + + /** * Records that an event occurred. * * @param event The event that occurred. @@ -188,6 +197,8 @@ private native void nativeIncrementSelectionAdds(long nativeJourneyLoggerAndroid, int section); private native void nativeSetCanMakePaymentValue( long nativeJourneyLoggerAndroid, boolean value); + private native void nativeSetHasEnrolledInstrumentValue( + long nativeJourneyLoggerAndroid, boolean value); private native void nativeSetEventOccurred(long nativeJourneyLoggerAndroid, int event); private native void nativeSetRequestedInformation(long nativeJourneyLoggerAndroid, boolean requestShipping, boolean requestEmail, boolean requestPhone,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java index c831680f..a9e64c68 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
@@ -61,7 +61,7 @@ public void retry(PaymentValidationErrors errors) {} @Override - public void canMakePayment() { + public void canMakePayment(boolean legacyMode) { if (mClient != null) { mClient.onCanMakePayment(CanMakePaymentQueryResult.CANNOT_MAKE_PAYMENT); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java index b2f3f05..db80fb76 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -228,6 +228,7 @@ private PaymentRequestClient mClient; private boolean mIsCanMakePaymentResponsePending; + private boolean mUseLegacyCanMakePayment; private boolean mIsHasEnrolledInstrumentResponsePending; private boolean mHasEnrolledInstrumentUsesPerMethodQuota; private boolean mIsCurrentPaymentRequestShowing; @@ -696,7 +697,10 @@ } if (mIsCanMakePaymentResponsePending) { - respondCanMakePaymentQuery(mArePaymentMethodsSupported); + // New canMakePayment doesn't need to wait for all apps to be queried. + if (!mUseLegacyCanMakePayment || queryApps.isEmpty()) { + respondCanMakePaymentQuery(mUseLegacyCanMakePayment); + } } if (mIsHasEnrolledInstrumentResponsePending && queryApps.isEmpty()) { @@ -1578,25 +1582,41 @@ * Called by the merchant website to check if the user has complete payment instruments. */ @Override - public void canMakePayment() { + public void canMakePayment(boolean legacyMode) { if (mClient == null) return; if (isFinishedQueryingPaymentApps()) { - respondCanMakePaymentQuery(mArePaymentMethodsSupported); + respondCanMakePaymentQuery(legacyMode); } else { mIsCanMakePaymentResponsePending = true; + mUseLegacyCanMakePayment = legacyMode; } } - private void respondCanMakePaymentQuery(boolean response) { + private void respondCanMakePaymentQuery(boolean legacyMode) { if (mClient == null) return; mIsCanMakePaymentResponsePending = false; - mClient.onCanMakePayment(response ? CanMakePaymentQueryResult.CAN_MAKE_PAYMENT - : CanMakePaymentQueryResult.CANNOT_MAKE_PAYMENT); - // TODO(https://crbug.com/915907): emit JourneyLogger event once the event names are - // updated. + boolean response = legacyMode ? mHasEnrolledInstrument : mArePaymentMethodsSupported; + + // Only need to enforce query quota in legacy mode. Per-method quota not supported. + if (legacyMode + && !CanMakePaymentQuery.canQuery(mWebContents, mTopLevelOrigin, + mPaymentRequestOrigin, mMethodData, /*perMethodQuota=*/false)) { + if (shouldEnforceCanMakePaymentQueryQuota()) { + mClient.onCanMakePayment(CanMakePaymentQueryResult.QUERY_QUOTA_EXCEEDED); + } else { + mClient.onCanMakePayment(response + ? CanMakePaymentQueryResult.WARNING_CAN_MAKE_PAYMENT + : CanMakePaymentQueryResult.WARNING_CANNOT_MAKE_PAYMENT); + } + } else { + mClient.onCanMakePayment(response ? CanMakePaymentQueryResult.CAN_MAKE_PAYMENT + : CanMakePaymentQueryResult.CANNOT_MAKE_PAYMENT); + } + + mJourneyLogger.setCanMakePaymentValue(response || mIsIncognito); if (sObserverForTest != null) { sObserverForTest.onPaymentRequestServiceCanMakePaymentQueryResponded(); @@ -1637,7 +1657,7 @@ : HasEnrolledInstrumentQueryResult.WARNING_HAS_NO_ENROLLED_INSTRUMENT); } - mJourneyLogger.setCanMakePaymentValue(response || mIsIncognito); + mJourneyLogger.setHasEnrolledInstrumentValue(response || mIsIncognito); if (sObserverForTest != null) { sObserverForTest.onPaymentRequestServiceHasEnrolledInstrumentQueryResponded(); @@ -1746,6 +1766,10 @@ ? 0 : SectionInformation.NO_SELECTION; + if (mIsCanMakePaymentResponsePending) { + respondCanMakePaymentQuery(mUseLegacyCanMakePayment); + } + if (mIsHasEnrolledInstrumentResponsePending) { respondHasEnrolledInstrumentQuery(mHasEnrolledInstrument); }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index f2a255fc..f040fc3b 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2793,9 +2793,6 @@ <message name="IDS_CAST_CASTING_VIDEO" desc="AtHome text to tell user which screen casting is happening. [CHAR LIMIT=40]"> Casting to <ph name="SCREEN_NAME">%1$s<ex>Living Room TV</ex></ph> </message> - <message name="IDS_CAST_ERROR_PLAYING_VIDEO" desc="The message shown to the user when playing a video to the Chromecast fails."> - Cannot play video on <ph name="SCREEN_NAME">%1$s<ex>Living Room TV</ex></ph>. - </message> <message name="IDS_ACCESSIBILITY_PLAY" desc="The play button that starts playing the media."> Play </message>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index e3f220c..3961578 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -896,20 +896,7 @@ "java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java", "java/src/org/chromium/chrome/browser/media/PictureInPicture.java", "java/src/org/chromium/chrome/browser/media/PictureInPictureController.java", - "java/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteController.java", - "java/src/org/chromium/chrome/browser/media/remote/CastNotificationControl.java", - "java/src/org/chromium/chrome/browser/media/remote/DefaultMediaRouteController.java", - "java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java", - "java/src/org/chromium/chrome/browser/media/remote/FullscreenMediaRouteButton.java", - "java/src/org/chromium/chrome/browser/media/remote/MediaRouteChooserDialogFactory.java", - "java/src/org/chromium/chrome/browser/media/remote/MediaRouteController.java", - "java/src/org/chromium/chrome/browser/media/remote/MediaRouteControllerDialogFactory.java", - "java/src/org/chromium/chrome/browser/media/remote/MediaUrlResolver.java", - "java/src/org/chromium/chrome/browser/media/remote/PositionExtrapolator.java", "java/src/org/chromium/chrome/browser/media/remote/RecordCastAction.java", - "java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerController.java", - "java/src/org/chromium/chrome/browser/media/remote/RemoteMediaPlayerWrapper.java", - "java/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfo.java", "java/src/org/chromium/chrome/browser/media/router/BaseMediaRouteDialogManager.java", "java/src/org/chromium/chrome/browser/media/router/CastRequestIdGenerator.java", "java/src/org/chromium/chrome/browser/media/router/CastSessionUtil.java", @@ -938,6 +925,7 @@ "java/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProvider.java", "java/src/org/chromium/chrome/browser/media/router/caf/CafMessageHandler.java", "java/src/org/chromium/chrome/browser/media/router/caf/CafNotificationController.java", + "java/src/org/chromium/chrome/browser/media/router/caf/CastMediaSource.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastOptionsProvider.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastSessionController.java", "java/src/org/chromium/chrome/browser/media/router/caf/CastUtils.java", @@ -945,21 +933,10 @@ "java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafExpandedControllerActivity.java", "java/src/org/chromium/chrome/browser/media/router/caf/remoting/CafRemotingMediaRouteProvider.java", "java/src/org/chromium/chrome/browser/media/router/caf/remoting/FlingingControllerAdapter.java", + "java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingMediaSource.java", "java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingNotificationController.java", "java/src/org/chromium/chrome/browser/media/router/caf/remoting/RemotingSessionController.java", "java/src/org/chromium/chrome/browser/media/router/caf/remoting/StreamPositionExtrapolator.java", - "java/src/org/chromium/chrome/browser/media/router/cast/BaseMediaRouteProvider.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastMediaSource.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandler.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastSession.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastSessionImpl.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CastSessionInfo.java", - "java/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManager.java", - "java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java", - "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java", - "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java", - "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java", "java/src/org/chromium/chrome/browser/media/ui/MediaButtonReceiver.java", "java/src/org/chromium/chrome/browser/media/ui/MediaImageCallback.java", "java/src/org/chromium/chrome/browser/media/ui/MediaImageManager.java", @@ -2087,12 +2064,6 @@ "javatests/src/org/chromium/chrome/browser/locale/LocaleManagerReferralTest.java", "javatests/src/org/chromium/chrome/browser/locale/LocaleManagerTest.java", "javatests/src/org/chromium/chrome/browser/media/MediaLauncherActivityTest.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastTestRule.java", - "javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java", "javatests/src/org/chromium/chrome/browser/media/router/MediaRouterIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/media/router/MockMediaRouteProvider.java", "javatests/src/org/chromium/chrome/browser/media/ui/AutoplayMutedNotificationTest.java", @@ -2478,9 +2449,6 @@ "junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java", "junit/src/org/chromium/chrome/browser/invalidation/ResumableDelayedTaskRunnerTest.java", "junit/src/org/chromium/chrome/browser/invalidation/SessionsInvalidationManagerTest.java", - "junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java", - "junit/src/org/chromium/chrome/browser/media/remote/MediaUrlResolverTest.java", - "junit/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfoTest.java", "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterRouteTest.java", "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterSinkObservationTest.java", "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterTestBase.java", @@ -2496,10 +2464,6 @@ "junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastContext.java", "junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastMediaSource.java", "junit/src/org/chromium/chrome/browser/media/router/caf/ShadowMediaRouter.java", - "junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java", - "junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaSourceTest.java", - "junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java", - "junit/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManagerTest.java", "junit/src/org/chromium/chrome/browser/media/ui/MediaImageManagerTest.java", "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationActionsUpdatedTest.java", "junit/src/org/chromium/chrome/browser/media/ui/MediaNotificationButtonComputationTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java deleted file mode 100644 index 2382567..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastNotificationTest.java +++ /dev/null
@@ -1,92 +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. - -package org.chromium.chrome.browser.media.remote; -import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.MAX_VIEW_TIME_MS; -import static org.chromium.chrome.browser.media.remote.CastTestRule.STABILIZE_TIME_MS; - -import android.support.test.filters.LargeTest; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.Restriction; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.chrome.browser.media.ui.MediaNotificationListener; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.media.MediaSwitches; - -import java.util.concurrent.TimeoutException; - -/** - * Tests of the notification. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, - "disable-features=" + MediaSwitches.USE_MODERN_MEDIA_CONTROLS}) -public class CastNotificationTest { - @Rule - public CastTestRule mCastTestRule = new CastTestRule(); - - private static final long PAUSE_TEST_TIME_MS = 1000; - - /** - * Test the pause button on the notification. - */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testNotificationPause() throws InterruptedException, TimeoutException { - mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - - // Get the notification - final CastNotificationControl notificationControl = mCastTestRule.waitForCastNotification(); - Assert.assertNotNull("No notificationTransportControl", notificationControl); - // Send pause - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - notificationControl.onPause(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION); - } - }); - Assert.assertTrue( - "Not paused", mCastTestRule.waitForState(PlayerState.PAUSED, MAX_VIEW_TIME_MS)); - - // The new position is sent in a separate message, so we have to wait a bit before - // fetching it. - Thread.sleep(STABILIZE_TIME_MS); - long position = mCastTestRule.getRemotePositionMs(); - // Position should not change while paused - Thread.sleep(PAUSE_TEST_TIME_MS); - Assert.assertEquals( - "Pause didn't stop playback", position, mCastTestRule.getRemotePositionMs()); - // Send play - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - notificationControl.onPlay(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION); - } - }); - Assert.assertTrue( - "Not playing", mCastTestRule.waitForState(PlayerState.PLAYING, MAX_VIEW_TIME_MS)); - - // Should now be running again. - Thread.sleep(PAUSE_TEST_TIME_MS); - Assert.assertTrue( - "Run didn't restart playback", position < mCastTestRule.getRemotePositionMs()); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java deleted file mode 100644 index e7cc0efa..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastPositionTransferTest.java +++ /dev/null
@@ -1,188 +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. - -package org.chromium.chrome.browser.media.remote; -import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.CAST_TEST_ROUTE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.MAX_VIEW_TIME_MS; -import static org.chromium.chrome.browser.media.remote.CastTestRule.VIDEO_ELEMENT; -import static org.chromium.chrome.browser.media.remote.CastTestRule.VIEW_RETRY_MS; - -import android.graphics.Rect; -import android.support.test.filters.LargeTest; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.Restriction; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.content_public.browser.test.util.JavaScriptUtils; -import org.chromium.media.MediaSwitches; - -import java.util.Date; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Tests related to the transfer of the playback position between the local and - * the remote player. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, - "disable-features=" + MediaSwitches.USE_MODERN_MEDIA_CONTROLS}) -@RetryOnFailure -public class CastPositionTransferTest { - @Rule - public CastTestRule mCastTestRule = new CastTestRule(); - - /** Reference position in the video where we should start casting */ - private static final int CAST_START_TIME_MS = 3000; - - /** Max accepted error for position comparisons. */ - private static final int SEEK_EPSILON_MS = 250; - - /** Used to wait for the UI to properly respond. It is smaller than the default - * {@link CastTestBase#STABILIZE_TIME_MS} to make sure the video doesn't finish while waiting.*/ - private static final int SMALL_STABILIZE_TIME_MS = 250; - - - /** Returns the current time of the video, in milliseconds, by getting it from JavaScript. */ - private static long getLocalPositionMillis(Tab tab, String videoElementId) - throws InterruptedException, TimeoutException, NumberFormatException { - StringBuilder sb = new StringBuilder(); - sb.append("(function() {"); - sb.append(" var node = document.getElementById('" + videoElementId + "');"); - sb.append(" if (node) return node.currentTime;"); - sb.append(" return null"); - sb.append("})();"); - String jsResult = JavaScriptUtils.executeJavaScriptAndWaitForResult( - tab.getWebContents(), sb.toString()); - return Math.round(Double.parseDouble(jsResult) * 1000); - } - - private static void seekFromJS(Tab tab, long seekToMs) - throws InterruptedException, TimeoutException, NumberFormatException { - final long seekToSec = TimeUnit.MILLISECONDS.toSeconds(seekToMs); - StringBuilder sb = new StringBuilder(); - sb.append("(function() {"); - sb.append(" var node = document.getElementById('" + VIDEO_ELEMENT + "');"); - sb.append(" if (node) node.currentTime = " + seekToSec + ";"); - sb.append(" return 0;"); - sb.append("})();"); - JavaScriptUtils.executeJavaScriptAndWaitForResult(tab.getWebContents(), sb.toString()); - } - - /** Test for crbug.com/428409 */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testLocalToRemotePositionTransfer() throws InterruptedException, TimeoutException { - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - final Rect videoRect = mCastTestRule.prepareDefaultVideofromPage(DEFAULT_VIDEO_PAGE, tab); - - // Jump to the position - seekFromJS(tab, CAST_START_TIME_MS); - final double localPositionMs = getLocalPositionMillis(tab, VIDEO_ELEMENT); - Assert.assertTrue("Local playback position (" + localPositionMs + ") did not advance past " - + CAST_START_TIME_MS, - localPositionMs >= CAST_START_TIME_MS); - - // Start cast - final long castStartTimeMs = new Date().getTime(); - mCastTestRule.castVideoAndWaitUntilPlaying(CAST_TEST_ROUTE, tab, videoRect); - - // Test the position - final long remotePositionMs = mCastTestRule.getRemotePositionMs(); - final long castDelayMs = new Date().getTime() - castStartTimeMs; - - Assert.assertTrue("The remote playback position (" + remotePositionMs - + ") did not advance past " + CAST_START_TIME_MS, - remotePositionMs >= CAST_START_TIME_MS); - Assert.assertTrue("The remote playback position (" + remotePositionMs + ") went too far.", - remotePositionMs <= CAST_START_TIME_MS + castDelayMs); - } - - /** Test for crbug.com/428409 */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testRemoteToLocalPositionTransfer() throws InterruptedException, TimeoutException { - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - final Rect videoRect = mCastTestRule.prepareDefaultVideofromPage(DEFAULT_VIDEO_PAGE, tab); - - // Start cast - mCastTestRule.castVideoAndWaitUntilPlaying(CAST_TEST_ROUTE, tab, videoRect); - - mCastTestRule.tapPlayPauseButton(tab, videoRect); - long pausePosition = mCastTestRule.getRemotePositionMs(); - for (int time = 0; time < MAX_VIEW_TIME_MS; time += VIEW_RETRY_MS) { - Thread.sleep(VIEW_RETRY_MS); - long newPosition = mCastTestRule.getRemotePositionMs(); - if (newPosition == pausePosition) { - break; - } - pausePosition = newPosition; - } - // Jump to the position - seekFromJS(tab, CAST_START_TIME_MS); - for (int time = 0; - time < MAX_VIEW_TIME_MS && mCastTestRule.getRemotePositionMs() != pausePosition; - time += VIEW_RETRY_MS) { - Thread.sleep(VIEW_RETRY_MS); - } - long remotePositionMs = mCastTestRule.getRemotePositionMs(); - Assert.assertEquals("The remote player did not seek", CAST_START_TIME_MS, remotePositionMs, - SEEK_EPSILON_MS); - - // Stop cast and play locally - final long castStopTimeMs = new Date().getTime(); - mCastTestRule.clickDisconnectFromRoute(tab, videoRect); - mCastTestRule.tapPlayPauseButton(tab, videoRect); - mCastTestRule.sleepNoThrow(SMALL_STABILIZE_TIME_MS); - - // Test the position - final double localPositionMs = getLocalPositionMillis(tab, VIDEO_ELEMENT); - final long castDelayMs = new Date().getTime() - castStopTimeMs; - Assert.assertTrue("The local playback position (" + localPositionMs - + ") did not advance past " + CAST_START_TIME_MS, - localPositionMs >= CAST_START_TIME_MS); - Assert.assertTrue("The local playback position (" + localPositionMs + ") went too far.", - localPositionMs <= CAST_START_TIME_MS + castDelayMs); - } - - /** Test for crbug.com/425105 */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/593840, crbug.com/652872 - public void testPositionUpdate() throws InterruptedException, TimeoutException { - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - final Rect videoRect = mCastTestRule.prepareDefaultVideofromPage(DEFAULT_VIDEO_PAGE, tab); - mCastTestRule.castVideoAndWaitUntilPlaying(CAST_TEST_ROUTE, tab, videoRect); - - mCastTestRule.sleepNoThrow(CAST_START_TIME_MS); - - final long remotePositionMs = mCastTestRule.getRemotePositionMs(); - final long localPositionMs = getLocalPositionMillis(tab, VIDEO_ELEMENT); - - Assert.assertEquals("Remote playback position was not properly updated.", - CAST_START_TIME_MS, remotePositionMs, SEEK_EPSILON_MS); - Assert.assertEquals("The remote playback position was updated, but the local one was not.", - CAST_START_TIME_MS, localPositionMs, SEEK_EPSILON_MS); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java deleted file mode 100644 index 4c854b1..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastStartStopTest.java +++ /dev/null
@@ -1,122 +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. - -package org.chromium.chrome.browser.media.remote; -import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.STABILIZE_TIME_MS; - -import android.graphics.Rect; -import android.support.test.filters.LargeTest; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisableIf; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.FlakyTest; -import org.chromium.base.test.util.Restriction; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.media.ui.MediaNotificationListener; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.media.MediaSwitches; - -import java.util.concurrent.TimeoutException; - -/** - * Simple tests of casting videos. These tests all use the same page, containing the same non-YT - * video. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, - "disable-features=" + MediaSwitches.USE_MODERN_MEDIA_CONTROLS}) -public class CastStartStopTest { - @Rule - public CastTestRule mCastTestRule = new CastTestRule(); - - /* - * Test that we can cast a video, and that we get the ExpandedControllerActivity when we do. - */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testCastingGenericVideo() throws InterruptedException, TimeoutException { - mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - mCastTestRule.checkVideoStarted(DEFAULT_VIDEO); - } - - /* - * Test that we can disconnect a cast session from the expanded controller activity overlay. - */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testStopFromVideoControls() throws InterruptedException, TimeoutException { - Rect videoRect = mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - - mCastTestRule.clickDisconnectFromRoute(tab, videoRect); - - mCastTestRule.checkDisconnected(); - } - - /* - * Test that we can stop a cast session from the notification. - */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testStopFromNotification() throws InterruptedException, TimeoutException { - mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - - // Get the notification - final CastNotificationControl notificationControl = mCastTestRule.waitForCastNotification(); - - // Send play - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - notificationControl.onStop(MediaNotificationListener.ACTION_SOURCE_MEDIA_SESSION); - } - }); - mCastTestRule.checkDisconnected(); - } - - /* - * Test that a cast session disconnects when the video ends - */ - @Test - @DisableIf.Build(sdk_is_less_than = 19, message = "crbug.com/582067") - @Feature({"VideoFling"}) - @LargeTest - @FlakyTest - public void testStopWhenVideoEnds() throws InterruptedException, TimeoutException { - mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - // Wait for the video to finish (this assumes the video is short, the test video - // is 8 seconds). - mCastTestRule.sleepNoThrow(STABILIZE_TIME_MS); - - Thread.sleep(mCastTestRule.getRemoteDurationMs()); - - // Everything should now have disconnected - mCastTestRule.checkDisconnected(); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java deleted file mode 100644 index f3cd818f..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastSwitchVideoTest.java +++ /dev/null
@@ -1,217 +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. - -package org.chromium.chrome.browser.media.remote; -import static org.chromium.chrome.browser.media.remote.CastTestRule.CAST_TEST_ROUTE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.TEST_VIDEO_2; -import static org.chromium.chrome.browser.media.remote.CastTestRule.TEST_VIDEO_PAGE_2; -import static org.chromium.chrome.browser.media.remote.CastTestRule.TWO_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.VIDEO_ELEMENT; - -import android.graphics.Rect; -import android.support.test.filters.LargeTest; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.content_public.browser.WebContents; -import org.chromium.content_public.browser.test.util.DOMUtils; -import org.chromium.media.MediaSwitches; - -import java.util.concurrent.TimeoutException; - -/** - * Test that other videos are played locally when casting - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, - "disable-features=" + MediaSwitches.USE_MODERN_MEDIA_CONTROLS}) -public class CastSwitchVideoTest { - @Rule - public CastTestRule mCastTestRule = new CastTestRule(); - - private static final String VIDEO_ELEMENT_2 = "video2"; - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testPlayNewVideoInNewTab() throws InterruptedException, TimeoutException { - checkPlaySecondVideo(DEFAULT_VIDEO_PAGE, VIDEO_ELEMENT, new Runnable() { - @Override - public void run() { - try { - mCastTestRule.loadUrlInNewTab( - mCastTestRule.getTestServer().getURL(TEST_VIDEO_PAGE_2)); - playVideoFromCurrentTab(VIDEO_ELEMENT); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testPlayNewVideoNewPageSameTab() throws InterruptedException, TimeoutException { - checkPlaySecondVideo(DEFAULT_VIDEO_PAGE, VIDEO_ELEMENT, new Runnable() { - @Override - public void run() { - try { - mCastTestRule.loadUrl(mCastTestRule.getTestServer().getURL(TEST_VIDEO_PAGE_2)); - playVideoFromCurrentTab(VIDEO_ELEMENT); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testPlayTwoVideosSamePage() throws InterruptedException, TimeoutException { - checkPlaySecondVideo(TWO_VIDEO_PAGE, VIDEO_ELEMENT_2, new Runnable() { - @Override - public void run() { - try { - playVideoFromCurrentTab(VIDEO_ELEMENT_2); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testCastNewVideoInNewTab() throws InterruptedException, TimeoutException { - checkCastSecondVideo(DEFAULT_VIDEO_PAGE, new Runnable() { - @Override - public void run() { - try { - mCastTestRule.loadUrlInNewTab( - mCastTestRule.getTestServer().getURL(TEST_VIDEO_PAGE_2)); - castVideoFromCurrentTab(VIDEO_ELEMENT); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testCastNewVideoNewPageSameTab() throws InterruptedException, TimeoutException { - checkCastSecondVideo(DEFAULT_VIDEO_PAGE, new Runnable() { - @Override - public void run() { - try { - mCastTestRule.loadUrl(mCastTestRule.getTestServer().getURL(TEST_VIDEO_PAGE_2)); - castVideoFromCurrentTab(VIDEO_ELEMENT); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure // crbug.com/623526 - public void testCastTwoVideosSamePage() throws InterruptedException, TimeoutException { - checkCastSecondVideo(TWO_VIDEO_PAGE, new Runnable() { - @Override - public void run() { - try { - castVideoFromCurrentTab(VIDEO_ELEMENT_2); - } catch (Exception e) { - Assert.fail("Failed to start second video; " + e.getMessage()); - } - } - }); - } - - private void checkPlaySecondVideo( - String firstVideoPage, String secondVideoId, final Runnable startSecondVideo) - throws InterruptedException, TimeoutException { - // TODO(aberent) Checking position is flaky, because it is timing dependent, but probably - // a good idea in principle. Need to find a way of unflaking it. - // int position = castAndPauseDefaultVideoFromPage(firstVideoPage); - mCastTestRule.castAndPauseDefaultVideoFromPage(firstVideoPage); - - startSecondVideo.run(); - - // Check that we are still casting the default video - Assert.assertEquals("The first video is not casting", - mCastTestRule.getTestServer().getURL(DEFAULT_VIDEO), mCastTestRule.getUriPlaying()); - - // Check that the second video is still there and paused - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - WebContents webContents = tab.getWebContents(); - Assert.assertFalse( - "Other video is not playing", DOMUtils.isMediaPaused(webContents, secondVideoId)); - } - - private void checkCastSecondVideo(String firstVideoPage, final Runnable startSecondVideo) - throws InterruptedException, TimeoutException { - // TODO(aberent) Checking position is flaky, because it is timing dependent, but probably - // a good idea in principle. Need to find a way of unflaking it. - // int position = castAndPauseDefaultVideoFromPage(firstVideoPage); - mCastTestRule.castAndPauseDefaultVideoFromPage(firstVideoPage); - - startSecondVideo.run(); - - // Check that we switch to playing the right video - mCastTestRule.checkVideoStarted(TEST_VIDEO_2); - } - - private void castVideoFromCurrentTab(String videoElement) throws InterruptedException, - TimeoutException { - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - WebContents webContents = tab.getWebContents(); - mCastTestRule.waitUntilVideoReady(videoElement, webContents); - Rect videoRect = DOMUtils.getNodeBounds(webContents, videoElement); - - mCastTestRule.castVideoAndWaitUntilPlaying(CAST_TEST_ROUTE, tab, videoRect); - } - - private void playVideoFromCurrentTab(String videoElement) throws InterruptedException, - TimeoutException { - WebContents webContents = mCastTestRule.getWebContents(); - - mCastTestRule.waitUntilVideoReady(videoElement, webContents); - - // Need to click on the video first to overcome the user gesture requirement. - DOMUtils.clickNode(webContents, videoElement); - DOMUtils.playMedia(webContents, videoElement); - DOMUtils.waitForMediaPlay(webContents, videoElement); - } - -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastTestRule.java deleted file mode 100644 index 48f93e9e..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastTestRule.java +++ /dev/null
@@ -1,548 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import android.app.Dialog; -import android.graphics.Rect; -import android.support.test.InstrumentationRegistry; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentManager; -import android.view.View; - -import org.junit.Assert; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.media.RouterTestUtils; -import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeActivityTestRule; -import org.chromium.content_public.browser.WebContents; -import org.chromium.content_public.browser.test.util.ClickUtils; -import org.chromium.content_public.browser.test.util.Coordinates; -import org.chromium.content_public.browser.test.util.DOMUtils; -import org.chromium.content_public.browser.test.util.JavaScriptUtils; -import org.chromium.content_public.browser.test.util.UiUtils; -import org.chromium.net.test.EmbeddedTestServer; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Custom TestRule for tests of Clank Cast. Contains functions for setting up a cast connection - * and other utility functions. - */ -public class CastTestRule extends ChromeActivityTestRule<ChromeActivity> { - private class TestListener implements MediaRouteController.UiListener { - @Override - public void onRouteSelected(String name, MediaRouteController mediaRouteController) {} - - @Override - public void onRouteUnselected(MediaRouteController mediaRouteController) {} - - @Override - public void onPrepared(MediaRouteController mediaRouteController) {} - - @Override - public void onError(int errorType, String message) {} - - @Override - public void onPlaybackStateChanged(final @PlayerState int newState) { - // Use postOnUiThread to handling the latch until the current UI task has completed, - // this makes sure that Cast has finished handling the event. - ThreadUtils.postOnUiThread(new Runnable() { - @Override - public void run() { - if ((mAwaitedStates.contains(newState))) { - mLatch.countDown(); - } - } - }); - } - - @Override - public void onDurationUpdated(long durationMillis) {} - - @Override - public void onPositionChanged(long positionMillis) {} - - @Override - public void onTitleChanged(String title) {} - } - - private Set<Integer> mAwaitedStates; - private CountDownLatch mLatch; - private EmbeddedTestServer mTestServer; - - // The name of the route provided by the dummy cast device. - public static final String CAST_TEST_ROUTE = "Cast Test Route"; - - // URLs of the default test page and video. - public static final String DEFAULT_VIDEO_PAGE = - "/chrome/test/data/android/media/simple_video.html"; - public static final String DEFAULT_VIDEO = "/chrome/test/data/android/media/test.webm"; - - // Constants used to find the default video and maximise button on the page - public static final String VIDEO_ELEMENT = "video"; - - // Max time to open a view. - public static final int MAX_VIEW_TIME_MS = 10000; - - // Time to let a video run to ensure that it has started. - public static final int RUN_TIME_MS = 1000; - // Time to allow for the UI to react to video controls, - public static final int STABILIZE_TIME_MS = 3000; - - // Retry interval when looking for a view. - public static final int VIEW_RETRY_MS = 100; - - public static final String TEST_VIDEO_PAGE_2 = - "/chrome/test/data/android/media/simple_video2.html"; - - public static final String TEST_VIDEO_2 = "/chrome/test/data/android/media/test2.webm"; - - public static final String TWO_VIDEO_PAGE = "/chrome/test/data/android/media/two_videos.html"; - - private static final String TAG = "CastTestRule"; - - private MediaRouteController mMediaRouteController; - - public CastTestRule() { - super(ChromeActivity.class); - } - - @Override - public Statement apply(final Statement base, Description description) { - return super.apply(new Statement() { - @Override - public void evaluate() throws Throwable { - setUp(); - base.evaluate(); - tearDown(); - } - }, description); - } - - private void setUp() throws Exception { - startMainActivityOnBlankPage(); - mTestServer = EmbeddedTestServer.createAndStartServer( - InstrumentationRegistry.getInstrumentation().getContext()); - } - - private void tearDown() throws Exception { - mTestServer.stopAndDestroyServer(); - } - - /** - * Wait for cast to reach a state we are interested in. - * Will deadlock if called on the target's UI thread. - * @param states - */ - public boolean waitForStates(final Set<Integer> states, int waitTimeMs) { - mAwaitedStates = states; - mLatch = new CountDownLatch(1); - // Deal with the case where Chrome is already in the desired state - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - if (mMediaRouteController != null - && states.contains(mMediaRouteController.getDisplayedPlayerState())) { - mLatch.countDown(); - } - } - }); - try { - return mLatch.await(waitTimeMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - return false; - } - } - - /** - * Wait for cast to reach a state we are interested in. - * Will deadlock if called on the target's UI thread. - * @param state - */ - public boolean waitForState(final @PlayerState int state, int waitTimeMs) { - Set<Integer> states = new HashSet<Integer>(); - states.add(state); - return waitForStates(states, waitTimeMs); - } - - @Override - public EmbeddedTestServer getTestServer() { - return mTestServer; - } - - public void castAndPauseDefaultVideoFromPage(String pagePath) - throws InterruptedException, TimeoutException { - Rect videoRect = castDefaultVideoFromPage(pagePath); - - final Tab tab = getActivity().getActivityTab(); - - Rect pauseButton = playPauseButton(videoRect); - - // Make sure the video has made some progress - Thread.sleep(RUN_TIME_MS); - - tapButton(tab, pauseButton); - Assert.assertTrue("Not paused", waitForState(PlayerState.PAUSED, MAX_VIEW_TIME_MS)); - } - - private boolean videoReady(String videoElement, WebContents webContents) { - // Create a javascript function to check if the video meta-data has been loaded. - StringBuilder sb = new StringBuilder(); - sb.append("(function() {"); - sb.append(" var node = document.getElementById('" + videoElement + "');"); - sb.append(" if (!node) return null;"); - // Any video readyState value greater than 0 means that at least the meta-data has been - // loaded but we also need the a document readyState of complete to ensure that page has - // been laid out with the correct video size, and everything is drawn. - sb.append(" return node.readyState > 0 && document.readyState == 'complete';"); - sb.append("})();"); - String javascriptResult; - try { - javascriptResult = - JavaScriptUtils.executeJavaScriptAndWaitForResult(webContents, sb.toString()); - Assert.assertFalse("Failed to retrieve contents for " + videoElement, - javascriptResult.trim().equalsIgnoreCase("null")); - - Boolean ready = javascriptResult.trim().equalsIgnoreCase("true"); - return ready; - } catch (InterruptedException e) { - Assert.fail("Interrupted"); - } catch (TimeoutException e) { - Assert.fail("Javascript execution timed out"); - } - return false; - } - - public void waitUntilVideoReady(String videoElement, WebContents webContents) { - for (int time = 0; time < MAX_VIEW_TIME_MS; time += VIEW_RETRY_MS) { - try { - if (videoReady(videoElement, webContents)) return; - } catch (Exception e) { - Assert.fail(e.toString()); - } - sleepNoThrow(VIEW_RETRY_MS); - } - Assert.fail("Video not ready"); - } - - public Rect prepareDefaultVideofromPage(String pagePath, Tab currentTab) - throws InterruptedException, TimeoutException { - loadUrl(mTestServer.getURL(pagePath)); - - WebContents webContents = currentTab.getWebContents(); - - waitUntilVideoReady(VIDEO_ELEMENT, webContents); - - return DOMUtils.getNodeBounds(webContents, VIDEO_ELEMENT); - } - - public Rect castDefaultVideoFromPage(String pagePath) - throws InterruptedException, TimeoutException { - final Tab tab = getActivity().getActivityTab(); - final Rect videoRect = prepareDefaultVideofromPage(pagePath, tab); - - castVideoAndWaitUntilPlaying(CAST_TEST_ROUTE, tab, videoRect); - - return videoRect; - } - - public void castVideoAndWaitUntilPlaying( - final String chromecastName, final Tab tab, final Rect videoRect) { - castVideo(chromecastName, tab, videoRect); - - Assert.assertTrue("Video didn't start playing", waitUntilPlaying()); - } - - public void castVideo(final String chromecastName, final Tab tab, final Rect videoRect) { - Log.i(TAG, "castVideo, videoRect = " + videoRect); - - ThreadUtils.runOnUiThreadBlocking(new Runnable() { - @Override - public void run() { - RemoteMediaPlayerController playerController = - RemoteMediaPlayerController.instance(); - mMediaRouteController = playerController.getMediaRouteController( - mTestServer.getURL(DEFAULT_VIDEO), mTestServer.getURL(DEFAULT_VIDEO_PAGE)); - Assert.assertNotNull("Could not get MediaRouteController", mMediaRouteController); - mMediaRouteController.addUiListener(new TestListener()); - } - }); - tapCastButton(tab, videoRect); - - // Wait for the test device to appear in the device list. - try { - UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation()); - } catch (InterruptedException e) { - Assert.fail(); - } - - View testRouteButton = RouterTestUtils.waitForRouteButton( - getActivity(), chromecastName, MAX_VIEW_TIME_MS, VIEW_RETRY_MS); - Assert.assertNotNull("Test route not found", testRouteButton); - - ClickUtils.mouseSingleClickView( - InstrumentationRegistry.getInstrumentation(), testRouteButton); - } - - public void checkDisconnected() { - HashSet<Integer> disconnectedStates = new HashSet<Integer>(); - disconnectedStates.add(PlayerState.FINISHED); - disconnectedStates.add(PlayerState.INVALIDATED); - waitForStates(disconnectedStates, MAX_VIEW_TIME_MS); - // Could use Assert.assertTrue(isDisconnected()) here, but retesting the individual aspects - // of disconnection gives more specific error messages. - CastNotificationControl notificationControl = CastNotificationControl.getForTests(); - if (notificationControl != null && notificationControl.isShowingForTests()) { - Assert.fail("Failed to close notification"); - } - Assert.assertEquals("Video still playing?", null, getUriPlaying()); - Assert.assertTrue("RemoteMediaPlayerController not stopped", !isPlayingRemotely()); - } - - public void clickDisconnectFromRoute(Tab tab, Rect videoRect) { - // Click on the cast control button to stop casting - tapCastButton(tab, videoRect); - - // Wait for the disconnect button - final View disconnectButton = RouterTestUtils.waitForView(new Callable<View>() { - @Override - public View call() { - FragmentManager fm = getActivity().getSupportFragmentManager(); - if (fm == null) return null; - DialogFragment mediaRouteControllerFragment = (DialogFragment) fm.findFragmentByTag( - "android.support.v7.mediarouter:MediaRouteControllerDialogFragment"); - if (mediaRouteControllerFragment == null) return null; - Dialog dialog = mediaRouteControllerFragment.getDialog(); - if (dialog == null) return null; - // The stop button (previously called disconnect) simply uses 'button1' in the - // latest version of the support library. See: - // https://cs.corp.google.com/#android/frameworks/support/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java&l=90. - // TODO(aberent) remove dependency on internals of support library - // https://crbug/548599 - return dialog.findViewById(android.R.id.button1); - } - }, MAX_VIEW_TIME_MS, VIEW_RETRY_MS); - - Assert.assertNotNull("No disconnect button", disconnectButton); - - ClickUtils.clickButton(disconnectButton); - } - - /* - * Check that a (non-YouTube) video has started playing, and that all the controls have been - * correctly set up. - */ - public void checkVideoStarted(String testVideo) { - // Check we have a notification - CastNotificationControl notificationControl = waitForCastNotification(); - Assert.assertNotNull("No notification controller", notificationControl); - Assert.assertTrue("No notification", notificationControl.isShowingForTests()); - // Check that we are playing the right video - waitUntilVideoCurrent(testVideo); - Assert.assertEquals("Wrong video playing", mTestServer.getURL(testVideo), getUriPlaying()); - - // Check that the RemoteMediaPlayerController and the (YouTube)MediaRouteController have - // been set up correctly - waitUntilPlaying(); - RemoteMediaPlayerController playerController = RemoteMediaPlayerController.getIfExists(); - Assert.assertNotNull("No RemoteMediaPlayerController", playerController); - Assert.assertTrue("Video not playing", isPlayingRemotely()); - Assert.assertTrue("Wrong sort of MediaRouteController", - (playerController.getCurrentlyPlayingMediaRouteController() - instanceof DefaultMediaRouteController)); - } - - public void sleepNoThrow(long timeout) { - try { - Thread.sleep(timeout); - } catch (InterruptedException e) { - Assert.fail(e.toString()); - } - } - - public void tapVideoFullscreenButton(final Tab tab, final Rect videoRect) { - tapButton(tab, fullscreenButton(videoRect)); - } - - public void tapCastButton(final Tab tab, final Rect videoRect) { - tapButton(tab, castButton(videoRect)); - } - - public void tapPlayPauseButton(final Tab tab, final Rect videoRect) { - tapButton(tab, playPauseButton(videoRect)); - } - - public CastNotificationControl waitForCastNotification() { - for (int time = 0; time < MAX_VIEW_TIME_MS; time += VIEW_RETRY_MS) { - CastNotificationControl result = ThreadUtils.runOnUiThreadBlockingNoException( - new Callable<CastNotificationControl>() { - @Override - public CastNotificationControl call() { - return CastNotificationControl.getForTests(); - } - }); - if (result != null) { - return result; - } - sleepNoThrow(VIEW_RETRY_MS); - } - return null; - } - - public boolean waitUntilPlaying() { - return waitForState(PlayerState.PLAYING, MAX_VIEW_TIME_MS); - } - - private boolean isDisconnected() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { - @Override - public Boolean call() { - CastNotificationControl notificationControl = CastNotificationControl.getForTests(); - if (notificationControl != null && notificationControl.isShowingForTests()) { - return false; - } - if (getUriPlaying() != null) return false; - return !isPlayingRemotely(); - } - }); - } - - private boolean waitUntilVideoCurrent(String testVideo) { - for (int time = 0; time < MAX_VIEW_TIME_MS; time += VIEW_RETRY_MS) { - if (mTestServer.getURL(testVideo).equals(getUriPlaying())) { - return true; - } - sleepNoThrow(VIEW_RETRY_MS); - } - return false; - } - - public String getUriPlaying() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<String>() { - @Override - public String call() { - if (mMediaRouteController == null) return ""; - return mMediaRouteController.getUriPlaying(); - } - }); - } - - public long getRemotePositionMs() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Long>() { - @Override - public Long call() { - return getMediaRouteController().getPosition(); - } - }); - } - - public long getRemoteDurationMs() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Long>() { - @Override - public Long call() { - return getMediaRouteController().getDuration(); - } - }); - } - - public boolean isPlayingRemotely() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { - @Override - public Boolean call() { - RemoteMediaPlayerController playerController = - RemoteMediaPlayerController.getIfExists(); - if (playerController == null) return false; - MediaRouteController routeController = - playerController.getCurrentlyPlayingMediaRouteController(); - if (routeController == null) return false; - return routeController.isPlaying(); - } - }); - } - - public MediaRouteController getMediaRouteController() { - return ThreadUtils.runOnUiThreadBlockingNoException(new Callable<MediaRouteController>() { - @Override - public MediaRouteController call() { - RemoteMediaPlayerController playerController = - RemoteMediaPlayerController.getIfExists(); - Assert.assertNotNull("No RemoteMediaPlayerController", playerController); - MediaRouteController routeController = - playerController.getCurrentlyPlayingMediaRouteController(); - Assert.assertNotNull("No MediaRouteController", routeController); - return routeController; - } - }); - } - - /* - * Functions to find the controls Unfortunately the controls are invisible to the code outside - * Blink, so this is highly dependent on the geometry defined in Blink css (see - * MediaControls.css & MediaControlsAndroid.css). - */ - private static final int CONTROLS_HEIGHT = 35; - private static final int BUTTON_WIDTH = 35; - private static final int CONTROL_BAR_MARGIN = 5; - private static final int BUTTON_RIGHT_MARGIN = 9; - private static final int PLAY_BUTTON_LEFT_MARGIN = 9; - private static final int FULLSCREEN_BUTTON_LEFT_MARGIN = -5; - - private Rect controlBar(Rect videoRect) { - int left = videoRect.left + CONTROL_BAR_MARGIN; - int right = videoRect.right - CONTROL_BAR_MARGIN; - int bottom = videoRect.bottom - CONTROL_BAR_MARGIN; - int top = videoRect.bottom - CONTROLS_HEIGHT; - return new Rect(left, top, right, bottom); - } - - private Rect playPauseButton(Rect videoRect) { - Rect bar = controlBar(videoRect); - int left = bar.left + PLAY_BUTTON_LEFT_MARGIN; - int right = left + BUTTON_WIDTH; - return new Rect(left, bar.top, right, bar.bottom); - } - - private Rect castButton(Rect videoRect) { - Rect bar = controlBar(videoRect); - int right = bar.right - BUTTON_RIGHT_MARGIN; - int left = right - BUTTON_WIDTH; - return new Rect(left, bar.top, right, bar.bottom); - } - - private Rect fullscreenButton(Rect videoRect) { - Rect downloadButton = downloadButton(videoRect); - int right = downloadButton.left; - int left = right - BUTTON_WIDTH; - return new Rect(left, downloadButton.top, right, downloadButton.bottom); - } - - private Rect downloadButton(Rect videoRect) { - Rect castButton = castButton(videoRect); - int right = castButton.right - BUTTON_RIGHT_MARGIN; - int left = right - BUTTON_WIDTH; - return new Rect(left, castButton.top, right, castButton.bottom); - } - - private void tapButton(Tab tab, Rect rect) { - Coordinates coord = Coordinates.createFor(tab.getWebContents()); - int clickX = (int) coord.fromLocalCssToPix(((float) (rect.left + rect.right)) / 2); - int clickY = (int) coord.fromLocalCssToPix(((float) (rect.top + rect.bottom)) / 2) - + getActivity().getCompositorViewHolder().getTopControlsHeightPixels(); - // Click using a virtual mouse, since a touch may result in a disambiguation pop-up. - ClickUtils.mouseSingleClickView( - InstrumentationRegistry.getInstrumentation(), tab.getView(), clickX, clickY); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java deleted file mode 100644 index 113223b..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/media/remote/CastVideoControlsTest.java +++ /dev/null
@@ -1,80 +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. - -package org.chromium.chrome.browser.media.remote; -import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.DEFAULT_VIDEO_PAGE; -import static org.chromium.chrome.browser.media.remote.CastTestRule.MAX_VIEW_TIME_MS; -import static org.chromium.chrome.browser.media.remote.CastTestRule.VIEW_RETRY_MS; - -import android.graphics.Rect; -import android.support.test.filters.LargeTest; - -import org.junit.Assert; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.Restriction; -import org.chromium.base.test.util.RetryOnFailure; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.media.MediaSwitches; - -import java.util.concurrent.TimeoutException; - -/** - * Instrumentation tests for the fullscreen cast controls. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, - "disable-features=" + MediaSwitches.USE_MODERN_MEDIA_CONTROLS}) -public class CastVideoControlsTest { - @Rule - public CastTestRule mCastTestRule = new CastTestRule(); - - private static final long PAUSE_TEST_TIME_MS = 1000; - - /* - * Test the pause button. - */ - @Test - @DisabledTest // crbug.com/907307 - @Feature({"VideoFling"}) - @LargeTest - @RetryOnFailure - @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE) // crbug.com/652872 - public void testPauseButton() throws InterruptedException, TimeoutException { - Rect videoRect = mCastTestRule.castDefaultVideoFromPage(DEFAULT_VIDEO_PAGE); - - final Tab tab = mCastTestRule.getActivity().getActivityTab(); - - mCastTestRule.tapPlayPauseButton(tab, videoRect); - // The new position is sent in a separate message, so we have to wait a bit before - // fetching it. - long position = mCastTestRule.getRemotePositionMs(); - boolean paused = false; - for (int time = 0; time < MAX_VIEW_TIME_MS; time += VIEW_RETRY_MS) { - Thread.sleep(VIEW_RETRY_MS); - long newPosition = mCastTestRule.getRemotePositionMs(); - if (newPosition == position) { - paused = true; - break; - } - position = newPosition; - } - // Check we have paused before the end of the video (with a fudge factor for timing - // variation) - Assert.assertTrue("Pause didn't stop playback", - paused || position < mCastTestRule.getRemoteDurationMs() - 100); - mCastTestRule.tapPlayPauseButton(tab, videoRect); - Thread.sleep(PAUSE_TEST_TIME_MS); - Assert.assertTrue( - "Run didn't restart playback", position < mCastTestRule.getRemotePositionMs()); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java index 8297c53..67e82ea 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -113,6 +113,7 @@ final CallbackHelper mBillingAddressChangeProcessed; final CallbackHelper mShowFailed; final CallbackHelper mCanMakePaymentQueryResponded; + final CallbackHelper mHasEnrolledInstrumentQueryResponded; final CallbackHelper mExpirationMonthChange; final CallbackHelper mPaymentResponseReady; PaymentRequestUI mUI; @@ -144,6 +145,7 @@ mPaymentResponseReady = new CallbackHelper(); mShowFailed = new CallbackHelper(); mCanMakePaymentQueryResponded = new CallbackHelper(); + mHasEnrolledInstrumentQueryResponded = new CallbackHelper(); mWebContentsRef = new AtomicReference<>(); mTestFilePath = testFileName.startsWith("data:") ? testFileName @@ -218,10 +220,7 @@ return mCanMakePaymentQueryResponded; } public CallbackHelper getHasEnrolledInstrumentQueryResponded() { - // TODO(https://crbug.com/915907): return mHasEnrolledInstrumentQueryResponded once - // hasEnrolledInstrument is exposed in the PaymentRequest JavaScript API and browser tests - // are switched over to use the new API. - return mCanMakePaymentQueryResponded; + return mHasEnrolledInstrumentQueryResponded; } public CallbackHelper getExpirationMonthChange() { return mExpirationMonthChange; @@ -919,10 +918,7 @@ @Override public void onPaymentRequestServiceHasEnrolledInstrumentQueryResponded() { ThreadUtils.assertOnUiThread(); - // TODO(https://crbug.com/915907): return mHasEnrolledInstrumentQueryResponded once - // hasEnrolledInstrument is exposed in the PaymentRequest JavaScript API and browser tests - // are switched over to use the new API. - mCanMakePaymentQueryResponded.notifyCalled(); + mHasEnrolledInstrumentQueryResponded.notifyCalled(); } @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java index d5c59aa..f229514 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/profiling_host/ProfilingProcessHostAndroidTest.java
@@ -43,36 +43,22 @@ public void testModeBrowser() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode( - "browser", false, "native-include-thread-names", true, false, false)); + Assert.assertTrue( + shim.runTestForMode("browser", false, "native-include-thread-names", false, false)); } @Test @MediumTest public void testModeBrowserDynamic() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "native", true, false, false)); - } - - @Test - @MediumTest - public void testModeBrowserDynamicNonStreaming() throws Exception { - HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "native", false, false, false)); + Assert.assertTrue(shim.runTestForMode("browser", true, "native", false, false)); } @Test @MediumTest public void testModeBrowserDynamicPseudo() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false, false)); - } - - @Test - @MediumTest - public void testModeBrowserDynamicPseudoNonStreaming() throws Exception { - HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false, false)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", false, false)); } // Non-browser processes must be profiled with a command line flag, since @@ -87,8 +73,7 @@ Add({"memlog=all-renderers", "memlog-stack-mode=pseudo", "memlog-sampling-rate=1"}) public void testModeRendererPseudo() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue( - shim.runTestForMode("all-renderers", false, "pseudo", true, false, false)); + Assert.assertTrue(shim.runTestForMode("all-renderers", false, "pseudo", false, false)); } @Test @@ -96,28 +81,27 @@ @CommandLineFlags.Add({"memlog=gpu", "memlog-stack-mode=pseudo", "memlog-sampling-rate=1"}) public void testModeGpuPseudo() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("gpu", false, "native", true, false, false)); + Assert.assertTrue(shim.runTestForMode("gpu", false, "native", false, false)); } @Test @MediumTest public void testModeBrowserDynamicPseudoSampleEverything() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, true)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true)); } @Test @MediumTest public void testModeBrowserDynamicPseudoSamplePartial() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, true, false)); + Assert.assertTrue(shim.runTestForMode("browser", true, "pseudo", true, false)); } @Test @MediumTest public void testModeBrowserAndAllUtility() throws Exception { HeapProfilingTestShim shim = new HeapProfilingTestShim(); - Assert.assertTrue( - shim.runTestForMode("utility-and-browser", true, "pseudo", true, true, false)); + Assert.assertTrue(shim.runTestForMode("utility-and-browser", true, "pseudo", true, false)); } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java deleted file mode 100644 index 5cf8fd2f..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java +++ /dev/null
@@ -1,460 +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. - -package org.chromium.chrome.browser.media.remote; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.Uri; -import android.support.v7.media.MediaItemStatus; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.Callback; -import android.support.v7.media.MediaRouter.ProviderInfo; -import android.support.v7.media.MediaRouter.RouteInfo; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.shadows.multidex.ShadowMultiDex; -import org.robolectric.util.ReflectionHelpers; - -import org.chromium.base.CommandLine; -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener; -import org.chromium.chrome.browser.media.remote.MediaRouteController.UiListener; - -/** Tests for {@link AbstractMediaRouteController}. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE, - shadows = {AbstractMediaRouteControllerTest.ShadowMediaRouter.class, ShadowMultiDex.class}) -public class AbstractMediaRouteControllerTest { - /** Reset the environment before each test. */ - @Before - public void beforeTest() { - // TODO(dgn): Remove when command line flags are not used anymore to detect debug - // see http://crbug.com/469649 - CommandLine.init(new String[] {}); - - - ShadowMediaRouter.sMediaRouter = null; - ShadowMediaRouter.sCallback = null; - DummyMediaRouteController.sMediaRouteSelector = mock(MediaRouteSelector.class); - } - - /** - * Test method for {@link AbstractMediaRouteController#isPlaying()}. - * - * Checks that it returns the correct value for all possible playback states. - */ - @Test - @Feature({"MediaRemote"}) - public void testIsPlaying() { - // Using a spy here to override some methods. - AbstractMediaRouteController mediaRouteCtrl = spy(new DummyMediaRouteController()); - - // Default - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_BUFFERING); - assertTrue(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_CANCELED); - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_ERROR); - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_FINISHED); - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_INVALIDATED); - assertFalse(mediaRouteCtrl.isPlaying()); - - doReturn(0L).when(mediaRouteCtrl).getPosition(); - doReturn(5000L).when(mediaRouteCtrl).getDuration(); - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED); - assertFalse(mediaRouteCtrl.isPlaying()); - - doReturn(5000L).when(mediaRouteCtrl).getPosition(); - doReturn(5000L).when(mediaRouteCtrl).getDuration(); - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED); - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PENDING); - assertFalse(mediaRouteCtrl.isPlaying()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING); - assertTrue(mediaRouteCtrl.isPlaying()); - } - - /** - * Test method for {@link AbstractMediaRouteController#isBeingCast()}. - * - * Checks that it returns the correct value for all possible playback states. - */ - @Test - @Feature({"MediaRemote"}) - public void testIsBeingCast() { - // Using a spy here to override some methods. - AbstractMediaRouteController mediaRouteCtrl = spy(new DummyMediaRouteController()); - - // Default - assertFalse(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPreparedForTesting(); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_BUFFERING); - assertTrue(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_CANCELED); - assertFalse(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_ERROR); - assertFalse(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_FINISHED); - assertFalse(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_INVALIDATED); - assertFalse(mediaRouteCtrl.isBeingCast()); - - doReturn(0L).when(mediaRouteCtrl).getPosition(); - doReturn(5000L).when(mediaRouteCtrl).getDuration(); - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED); - assertTrue(mediaRouteCtrl.isBeingCast()); - - doReturn(5000L).when(mediaRouteCtrl).getPosition(); - doReturn(5000L).when(mediaRouteCtrl).getDuration(); - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED); - assertFalse(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PENDING); - assertTrue(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING); - assertTrue(mediaRouteCtrl.isBeingCast()); - - mediaRouteCtrl.setUnprepared(); - assertFalse(mediaRouteCtrl.isBeingCast()); - } - - /** Test method for {@link AbstractMediaRouteController#isRemotePlaybackAvailable()}.*/ - @Test - @Feature({"MediaRemote"}) - public void testIsRemotePlaybackAvailable() { - MediaRouter mediaRouter = mock(MediaRouter.class); - AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController(); - when(mediaRouter.getSelectedRoute()) - .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)); - when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())) - .thenReturn(true); - - // Default - assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable()); - - ShadowMediaRouter.sMediaRouter = mediaRouter; - mediaRouteCtrl = spy(new DummyMediaRouteController()); - assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable()); - - when(mediaRouter.getSelectedRoute()) - .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL)); - assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable()); - - when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())) - .thenReturn(false); - assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable()); - - when(mediaRouter.getSelectedRoute()) - .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)); - assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable()); - } - - /** - * Test method for - * {@link AbstractMediaRouteController#addMediaStateListener(MediaStateListener)} and - * {@link AbstractMediaRouteController#removeMediaStateListener(MediaStateListener)} - * - * Makes sure that listeners gets notified when they are added and don't get notified - * when removed. - */ - @Test - @Feature({"MediaRemote"}) - public void testAddRemoveMediaStateListener() { - MediaStateListener listener = mock(MediaStateListener.class); - MediaStateListener otherListener = mock(MediaStateListener.class); - MediaRouter mediaRouter = ShadowMediaRouter.createCapturingMock(); - when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())) - .thenReturn(true); - ShadowMediaRouter.sMediaRouter = mediaRouter; - - // Creating the MediaRouteController - AbstractMediaRouteController mediaRouteController = new DummyMediaRouteController(); - assertNotNull(mediaRouteController.getMediaRouter()); - - // 1. #addMediaStateListener() - mediaRouteController.addMediaStateListener(listener); - - // The route selector should get notified of new states. - verify(mediaRouter) - .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt()); - verify(listener).onRouteAvailabilityChanged(true); - - // Check the behavior difference between the first and subsequent additions. - mediaRouteController.addMediaStateListener(otherListener); - verify(otherListener).onRouteAvailabilityChanged(true); - // The call count should not change. - verify(mediaRouter) - .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt()); - verify(listener).onRouteAvailabilityChanged(true); - - // 2. #removeMediaStateListener() - mediaRouteController.removeMediaStateListener(otherListener); - - // The removed listener should not be notified of changes anymore. - when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())) - .thenReturn(false); - ShadowMediaRouter.sCallback.onRouteRemoved(mediaRouter, null); - verifyNoMoreInteractions(otherListener); - verify(listener).onRouteAvailabilityChanged(false); - verify(mediaRouter, times(0)).removeCallback(any(Callback.class)); - - mediaRouteController.removeMediaStateListener(listener); - - when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt())) - .thenReturn(true); - ShadowMediaRouter.sCallback.onRouteAdded(mediaRouter, null); - verifyNoMoreInteractions(otherListener); - verifyNoMoreInteractions(listener); - verify(mediaRouter).removeCallback(any(Callback.class)); - } - - /** - * Test method for - * {@link AbstractMediaRouteController#addMediaStateListener(MediaStateListener)} - * - * Tests that listeners are not used (state not initialized) when the media router - * is not initialized. - */ - @Test - @Feature({"MediaRemote"}) - public void testAddMediaStateListenerInitFailed() { - MediaStateListener listener = mock(MediaStateListener.class); - - AbstractMediaRouteController mediaRouteController = new DummyMediaRouteController(); - mediaRouteController.addMediaStateListener(listener); - - verify(listener, never()).onRouteAvailabilityChanged(anyBoolean()); - } - - /** Test method for {@link AbstractMediaRouteController#prepareMediaRoute()}.*/ - @Test - @Feature({"MediaRemote"}) - public void testPrepareMediaRoute() { - // Check when no media router, check that not done twice - - ShadowMediaRouter.sMediaRouter = mock(MediaRouter.class); - AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController(); - - verify(ShadowMediaRouter.sMediaRouter, times(0)) - .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt()); - - mediaRouteCtrl.prepareMediaRoute(); - verify(ShadowMediaRouter.sMediaRouter, times(1)) - .addCallback(eq(DummyMediaRouteController.sMediaRouteSelector), any(Callback.class), - eq(MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY)); - - mediaRouteCtrl.prepareMediaRoute(); - verify(ShadowMediaRouter.sMediaRouter, times(1)) - .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt()); - } - - /** - * Test method for {@link AbstractMediaRouteController#addUiListener(UiListener)} and - * {@link AbstractMediaRouteController#removeMediaStateListener(MediaStateListener)}. - */ - @Test - @Feature({"MediaRemote"}) - public void testAddRemoveUiListener() { - DummyMediaRouteController mediaRouteCtrl = new DummyMediaRouteController(); - UiListener listener = mock(UiListener.class); - - mediaRouteCtrl.addUiListener(listener); - mediaRouteCtrl.verifyListenerActivation(1, listener); - - // Should not be added twice. - mediaRouteCtrl.addUiListener(listener); - mediaRouteCtrl.verifyListenerActivation(2, listener); - - mediaRouteCtrl.removeUiListener(listener); - mediaRouteCtrl.verifyListenerActivation(2, listener); - } - - private static RouteInfo createRouteInfo(int playbackType) { - Class<?>[] paramClasses = new Class[] {ProviderInfo.class, String.class, String.class}; - Object[] paramValues = new Object[] {null, "", ""}; - RouteInfo routeInfo = ReflectionHelpers.callConstructor(RouteInfo.class, - ReflectionHelpers.ClassParameter.fromComponentLists(paramClasses, paramValues)); - ReflectionHelpers.setField(routeInfo, "mPlaybackType", playbackType); - return routeInfo; - } - - /** Shadow needed because getInstance() can't be mocked */ - @Implements(MediaRouter.class) - public static class ShadowMediaRouter { - public static MediaRouter sMediaRouter; - public static Callback sCallback; - - @Implementation - public static MediaRouter getInstance(Context context) { - return sMediaRouter; - } - - /** - * Creates a {@link MediaRouter} mock that will capture the callback argument - * when {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)} is called - * and sets it to {@link #sCallback}. - */ - public static MediaRouter createCapturingMock() { - MediaRouter mediaRouter = mock(MediaRouter.class); - - final ArgumentCaptor<Callback> callbackArg = ArgumentCaptor.forClass(Callback.class); - final Answer<?> addCallbackAnswer = new Answer<Object>() { - @Override - public Object answer(InvocationOnMock invocation) { - sCallback = callbackArg.getValue(); - return null; - } - }; - - Mockito.doAnswer(addCallbackAnswer) - .when(mediaRouter) - .addCallback(any(MediaRouteSelector.class), callbackArg.capture(), anyInt()); - return mediaRouter; - } - } - - /** - * A dummy class used here to be able to instantiate the abstract class while calling its - * constructor. {@link AbstractMediaRouteController#buildMediaRouteSelector()} needs to be - * implemented before the object's construction. Mockito's mocks don't call constructors. - */ - private static class DummyMediaRouteController extends AbstractMediaRouteController { - /** - * MediaRouteSelector required during MediaRouteController's constructor call via - * {@link #buildMediaRouteSelector()} - */ - public static MediaRouteSelector sMediaRouteSelector; - - @Override - public boolean initialize() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean canPlayMedia(String sourceUrl, String frameUrl) { - throw new UnsupportedOperationException(); - } - - @Override - public MediaRouteSelector buildMediaRouteSelector() { - return sMediaRouteSelector; - } - - @Override - public boolean reconnectAnyExistingRoute() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDataSource(Uri uri, String cookies) { - throw new UnsupportedOperationException(); - } - - @Override - public void prepareAsync(String frameUrl, long startPositionMillis) { - throw new UnsupportedOperationException(); - } - - @Override - public void setRemoteVolume(int delta) { - throw new UnsupportedOperationException(); - } - - @Override - public void resume() { - throw new UnsupportedOperationException(); - } - - @Override - public void pause() { - throw new UnsupportedOperationException(); - } - - @Override - public long getPosition() { - throw new UnsupportedOperationException(); - } - - @Override - public long getDuration() { - throw new UnsupportedOperationException(); - } - - @Override - public void seekTo(long msec) { - throw new UnsupportedOperationException(); - } - - @Override - public void release() { - throw new UnsupportedOperationException(); - } - - @Override - protected void onRouteAddedEvent(MediaRouter router, RouteInfo route) { - throw new UnsupportedOperationException(); - } - - @Override - public void onRouteSelected(MediaStateListener player, MediaRouter router, - RouteInfo route) { - throw new UnsupportedOperationException(); - } - - @Override - protected void onRouteUnselectedEvent(MediaRouter router, RouteInfo route) { - throw new UnsupportedOperationException(); - } - - public void verifyListenerActivation(int times, UiListener listener) { - String testTitle = "foo"; - updateTitle(testTitle); // protected methods, needs to be called within the class. - verify(listener, times(times)).onTitleChanged(testTitle); - } - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/MediaUrlResolverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/MediaUrlResolverTest.java deleted file mode 100644 index 024ed80..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/MediaUrlResolverTest.java +++ /dev/null
@@ -1,393 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; - -import android.net.Uri; - -import com.google.android.collect.Lists; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowApplication; - -import org.chromium.base.CommandLine; -import org.chromium.base.task.AsyncTask; -import org.chromium.base.task.test.CustomShadowAsyncTask; -import org.chromium.base.test.BaseRobolectricTestRunner; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Unit tests (run on host) for {@link org.chromium.chrome.browser.media.remote.MediaUrlResolver}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class}) -public class MediaUrlResolverTest { - // Constants copied from MediaUrlResolver. Don't use the copies in MediaUrlResolver - // since we want the tests to detect if these are changed or corrupted. - private static final String COOKIES_HEADER_NAME = "Cookies"; - private static final String CORS_HEADER_NAME = "Access-Control-Allow-Origin"; - private static final String RANGE_HEADER_NAME = "Range"; - private static final String RANGE_HEADER_VALUE = "bytes=0-65536"; - - private static final String USER_AGENT_HEADER_NAME = "User-Agent"; - private static final String CHROMECAST_ORIGIN = "https://www.gstatic.com"; - - private class TestDelegate implements MediaUrlResolver.Delegate { - private final String mCookies; - private final Uri mInputUri; - - private boolean mReturnedPlayable; - private Uri mReturnedUri; - - TestDelegate(Uri uri, String cookies) { - mInputUri = uri; - mCookies = cookies; - } - - @Override - public String getCookies() { - return mCookies; - } - - @Override - public Uri getUri() { - return mInputUri; - } - - @Override - public void deliverResult(Uri uri, boolean playable) { - mReturnedUri = uri; - mReturnedPlayable = playable; - } - - Uri getReturnedUri() { - return mReturnedUri; - } - - boolean isPlayable() { - return mReturnedPlayable; - } - } - - /** - * Dummy HttpURLConnection class that returns canned headers. Also prevents the test from going - * out to the real network. - */ - private class DummyUrlConnection extends HttpURLConnection { - - private final URL mUrl; - - protected DummyUrlConnection(URL u) { - super(u); - mUrl = u; - } - - @Override - public void connect() throws IOException { - if (mThrowIOException) throw new IOException(); - } - - @Override - public void disconnect() { - } - - @Override - public Map<String, List<String>> getHeaderFields() { - return mReturnedHeaders; - } - - @Override - public URL getURL() { - return mReturnedUrl == null ? mUrl : mReturnedUrl; - } - - @Override - public void setRequestProperty(String key, String value) { - mRequestProperties.put(key, value); - } - - @Override - public boolean usingProxy() { - return false; - } - - @Override - public int getResponseCode() { - return mReturnedResponseCode; - } - - } - - /** - * Class for plugging in DummyUrlConnection - */ - private class DummyUrlStreamHandler extends URLStreamHandler { - - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new DummyUrlConnection(u); - } - - } - - private Map<String, String> mRequestProperties; - - private Map<String, List<String>> mReturnedHeaders; - - private URL mReturnedUrl; - - private int mReturnedResponseCode; - - private boolean mThrowIOException; - - @Before - public void setup() { - // Initialize the command line to avoid a crash when the code checks the logging flag. - CommandLine.init(new String[0]); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with valid MPEG4 URL - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_validMpeg4() throws MalformedURLException { - // A valid mpeg4 URI is playable and unchanged. - Uri uri = Uri.parse("http://example.com/test.mp4"); - TestDelegate delegate = resolveUri(uri, null, 200, null, null, false); - assertThat("A valid mp4 uri is unchanged", delegate.getReturnedUri(), equalTo(uri)); - assertThat("A valid mp4 uri is playable", delegate.isPlayable(), equalTo(true)); - - // Check that the correct message was sent - assertThat("The message contained the user agent name", - mRequestProperties.get(USER_AGENT_HEADER_NAME), equalTo("User agent")); - assertThat("The message contained the range header", - mRequestProperties.get(RANGE_HEADER_NAME), equalTo(RANGE_HEADER_VALUE)); - assertThat("The message didn't contain any cookies", - mRequestProperties.get(COOKIES_HEADER_NAME), nullValue()); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} testing a null URL. - */ - @Test - public void testMediaUrlResolver_nullUri() { - // An null URL isn't playable - TestDelegate delegate = resolveUri(null, null, 200, null, null, false); - - assertThat("An empty uri remains empty", delegate.getReturnedUri(), equalTo(Uri.EMPTY)); - assertThat("An empty uri isn't playable", delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} testing empty URL. - */ - @Test - public void testMediaUrlResolver_emptyUri() { - // An empty URL isn't playable - TestDelegate delegate = resolveUri(Uri.EMPTY, null, 200, null, null, false); - - assertThat("An empty uri remains empty", delegate.getReturnedUri(), equalTo(Uri.EMPTY)); - assertThat("An empty uri isn't playable", delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} setting the cookies - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_cookies() throws MalformedURLException { - // Check that cookies are sent correctly. - Uri uri = Uri.parse("http://example.com/test.mp4"); - TestDelegate delegate = resolveUri(uri, "Cookies!", 200, null, null, false); - - assertThat("The message contained the cookies", - mRequestProperties.get(COOKIES_HEADER_NAME), equalTo("Cookies!")); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with a valid HLS URL and - * the Range-Request header. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_validHLSNoRange() throws MalformedURLException { - // Don't set range request header for manifest URLs like HLS. - Uri uri = Uri.parse("http://example.com/test.m3u8"); - TestDelegate delegate = resolveUri(uri, null, 200, null, null, false); - assertThat("The message didn't have the range header", - mRequestProperties.get(RANGE_HEADER_NAME), nullValue()); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} updating the URL in case of - * redirects. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_redirect() throws MalformedURLException { - // A redirected URI is retuend and is playable. - Uri uri = Uri.parse("http://example.com/test.mp4"); - URL returnedUri = new URL("http://cdn.example.com/foo/test.mp4"); - TestDelegate delegate = resolveUri(uri, null, 200, returnedUri, null, false); - - assertThat("A redirected uri is returned", - delegate.getReturnedUri(), equalTo(Uri.parse(returnedUri.toString()))); - assertThat("A redirected uri is playable", delegate.isPlayable(), equalTo(true)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} testing bad response code. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_serverError() { - // A valid URL is unplayable and an empty URL is returned if the server request fails. - Uri uri = Uri.parse("http://example.com/test.mp4"); - TestDelegate delegate = resolveUri(uri, null, 404, null, null, false); - - assertThat("An empty uri is returned on server error", - delegate.getReturnedUri(), equalTo(Uri.EMPTY)); - assertThat("Server error means unplayable uri", delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} when a network error happens. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_networkError() throws MalformedURLException { - // A random, non parsable, URI of unknown type is treated as not playable. - Uri uri = Uri.parse("http://example.com/test.mp4"); - TestDelegate delegate = resolveUri(uri, null, 404, null, null, true); - - assertThat("An empty uri is returned on network error", - delegate.getReturnedUri(), equalTo(Uri.EMPTY)); - assertThat("Network error means unplayable uri", delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with valid MPEG4 URL and compatible - * CORS header in the response. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_validMpeg4CompatibleCors() throws MalformedURLException { - // If a compatible CORS header returned, a valid mpeg4 URI is playable and unchanged. - Uri uri = Uri.parse("http://example.com/test.mp4"); - HashMap<String, List<String>> corsHeaders = new HashMap<String, List<String>>(); - corsHeaders.put(CORS_HEADER_NAME, Lists.newArrayList(CHROMECAST_ORIGIN)); - TestDelegate delegate = resolveUri(uri, null, 200, null, corsHeaders, false); - assertThat("A valid mp4 uri with CORS is unchanged", - delegate.getReturnedUri(), equalTo(uri)); - assertThat("A valid mp4 uri with CORS is playable", delegate.isPlayable(), equalTo(true)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with valid MPEG4 URL and - * incompatible CORS header in the response. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_validMpeg4InompatilbeCors() throws MalformedURLException { - // If an incompatible CORS header returned, a valid mpeg4 URI is not playable but unchanged. - Uri uri = Uri.parse("http://example.com/test.mp4"); - HashMap<String, List<String>> corsHeaders = new HashMap<String, List<String>>(); - corsHeaders.put(CORS_HEADER_NAME, Lists.newArrayList("http://google.com")); - - TestDelegate delegate = resolveUri(uri, null, 200, null, corsHeaders, false); - assertThat("A valid mp4 uri with incompatible CORS is unchanged", - delegate.getReturnedUri(), equalTo(uri)); - assertThat("A valid mp4 uri with incompatible CORS is not playable", - delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with a valid HLS URL and no CORS. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_validHLSNoCors() throws MalformedURLException { - // A valid mpeg4 URI is playable and unchanged. - Uri uri = Uri.parse("http://example.com/test.m3u8"); - TestDelegate delegate = resolveUri(uri, null, 200, null, null, false); - - assertThat("A valid HLS uri with no CORS is unchanged", - delegate.getReturnedUri(), equalTo(uri)); - assertThat("A valid HLS uri with no CORS is not playable", - delegate.isPlayable(), equalTo(false)); - } - - /** - * Test method for {@link MediaUrlResolver#MediaUrlResolver} with an unknown media type. - * - * @throws MalformedURLException - */ - @Test - public void testMediaUrlResolver_unknownMediaType() throws MalformedURLException { - // A URI with an unknown media type is unchanged but not playable. - Uri uri = Uri.parse("http://example.com/test.foo"); - TestDelegate delegate = resolveUri(uri, null, 200, null, null, false); - - assertThat("A valid uri with unknown media type is unchanged", - delegate.getReturnedUri(), equalTo(uri)); - assertThat("A valid uri with unknown media type is not playable", - delegate.isPlayable(), equalTo(false)); - } - - private TestDelegate resolveUri( - final Uri uri, - final String cookies, - int responseCode, - URL returnedUrl, - Map<String, List<String>> returnedHeaders, - boolean throwIOException) { - mReturnedResponseCode = responseCode; - mReturnedUrl = returnedUrl; - mReturnedHeaders = returnedHeaders == null ? new HashMap<String, List<String>>() - : returnedHeaders; - mRequestProperties = new HashMap<String, String>(); - mThrowIOException = throwIOException; - - TestDelegate delegate = new TestDelegate(uri, cookies); - MediaUrlResolver resolver = new MediaUrlResolver(delegate, "User agent", - new DummyUrlStreamHandler()) { - // The RecordHistogram members are static, and call native, so we must prevent any calls - // to them by overriding the wrapper. - @Override - void recordResultHistogram(int result) { - } - }; - resolver.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - ShadowApplication.runBackgroundTasks(); - - return delegate; - } - -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfoTest.java deleted file mode 100644 index 00e38eed..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/RemoteVideoInfoTest.java +++ /dev/null
@@ -1,96 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.remote; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; - -/** - * Unit tests (run on host) for {@link RemoteVideoInfo}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class RemoteVideoInfoTest { - /** - * Test method for {@link RemoteVideoInfo#hashCode()}. - */ - @Test - public void testHashCode() { - RemoteVideoInfo i1 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, - 50, null); - int h1 = i1.hashCode(); - - RemoteVideoInfo i2 = new RemoteVideoInfo(i1); - assertThat("Copying RemoteVideoInfo preserves the hash code", i2.hashCode(), equalTo(h1)); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, - 50, null); - assertThat("Identical RemoteVideoInfos the same hash code", i2.hashCode(), equalTo(h1)); - - int ho = new Object().hashCode(); - assertThat("Objects of different types are unequal to RemoteVideoInfos", i1.hashCode(), - not(equalTo(ho))); - - i2 = new RemoteVideoInfo("Test1", 100, RemoteVideoInfo.PlayerState.STOPPED, 50, null); - assertThat("Changing title changes hash code", i2.hashCode(), not(equalTo(h1))); - - i2 = new RemoteVideoInfo("Test", 200, RemoteVideoInfo.PlayerState.STOPPED, 50, null); - assertThat("Changing duration changes hash code", i2.hashCode(), not(equalTo(h1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.INVALIDATED, 50, null); - assertThat("Changing state changes hash code", i2.hashCode(), not(equalTo(h1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, 70, null); - assertThat("Changing current time changes hash code", i2.hashCode(), not(equalTo(h1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, 50, "Error"); - assertThat("Changing error changes hash code", i2.hashCode(), not(equalTo(h1))); - } - - /** - * Test method for - * {@link RemoteVideoInfo#equals(java.lang.Object)}. - */ - @Test - public void testEqualsObject() { - RemoteVideoInfo i1 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, - 50, null); - assertThat("A RemoteVideoInfo is equal to itself", i1, equalTo(i1)); - - RemoteVideoInfo i2 = new RemoteVideoInfo(i1); - assertThat("A RemoteVideoInfo is equal to its copy", i2, equalTo(i1)); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, - 50, null); - assertThat("identical RemoteVideoInfos are equal", i2, equalTo(i1)); - - Object o = new Object(); - assertThat("Objects of different types are unequal to RemoteVideoInfos", i1, - not(equalTo(o))); - - i2 = new RemoteVideoInfo("Test1", 100, RemoteVideoInfo.PlayerState.STOPPED, 50, null); - assertThat("Changing title makes RemoteVideoInfos unequal", i2, not(equalTo(i1))); - - i2 = new RemoteVideoInfo("Test", 200, RemoteVideoInfo.PlayerState.STOPPED, 50, null); - assertThat("Changing duration makes RemoteVideoInfos unequal", i2, not(equalTo(i1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.INVALIDATED, 50, null); - assertThat("Changing state makes RemoteVideoInfos unequal", i2, not(equalTo(i1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, 70, null); - assertThat("Changing current time makes RemoteVideoInfos unequal", i2, not(equalTo(i1))); - - i2 = new RemoteVideoInfo("Test", 100, RemoteVideoInfo.PlayerState.STOPPED, 50, "Error"); - assertThat("Changing error makes RemoteVideoInfos unequal", i2, not(equalTo(i1))); - } - -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java index 532dd83..d7bfd8a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/CafMediaRouteProviderTest.java
@@ -46,7 +46,6 @@ import org.chromium.chrome.browser.media.router.MediaRoute; import org.chromium.chrome.browser.media.router.MediaRouteManager; import org.chromium.chrome.browser.media.router.MediaSink; -import org.chromium.chrome.browser.media.router.cast.CastMediaSource; /** * Robolectric tests for CafMediaRouteProvider.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastMediaSource.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastMediaSource.java index 4907a979..6d9f698a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastMediaSource.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/caf/ShadowCastMediaSource.java
@@ -7,8 +7,6 @@ import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -import org.chromium.chrome.browser.media.router.cast.CastMediaSource; - /** Shadow implementation for {@link MediaRouter}. */ @Implements(CastMediaSource.class) public class ShadowCastMediaSource {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java deleted file mode 100644 index 02631a79..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java +++ /dev/null
@@ -1,155 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.support.v7.media.MediaRouter; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.media.router.ChromeMediaRouter; -import org.chromium.chrome.browser.media.router.MediaRoute; -import org.chromium.chrome.browser.media.router.MediaRouteManager; -import org.chromium.chrome.browser.media.router.MediaSink; - -import java.util.ArrayList; - -/** - * Robolectric tests for {@link CastMediaRouteProvider}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class CastMediaRouteProviderTest { - private static final String SUPPORTED_SOURCE = "cast:DEADBEEF"; - - private static final String SUPPORTED_AUTOJOIN_SOURCE = "cast:DEADBEEF" - + "?clientId=12345&autoJoinPolicy=" + CastMediaSource.AUTOJOIN_TAB_AND_ORIGIN_SCOPED; - - // TODO(crbug.com/672704): Android does not currently support 1-UA mode. - private static final String UNSUPPORTED_SOURCE = "https://example.com"; - - private MediaRouteManager mMockManager; - private CastMediaRouteProvider mProvider; - - protected void setUpMediaRouter(MediaRouter router) { - ChromeMediaRouter.setAndroidMediaRouterForTest(router); - mMockManager = mock(MediaRouteManager.class); - mProvider = CastMediaRouteProvider.create(mMockManager); - } - - @Test - @Feature({"MediaRouter"}) - public void testStartObservingMediaSinksNoMediaRouter() { - setUpMediaRouter(null); - - mProvider.startObservingMediaSinks(SUPPORTED_SOURCE); - - verify(mMockManager, timeout(100)) - .onSinksReceived( - eq(SUPPORTED_SOURCE), same(mProvider), eq(new ArrayList<MediaSink>())); - } - - @Test - @Feature({"MediaRouter"}) - public void testStartObservingMediaSinksUnsupportedSource() { - setUpMediaRouter(mock(MediaRouter.class)); - - mProvider.startObservingMediaSinks(UNSUPPORTED_SOURCE); - - verify(mMockManager, timeout(100)) - .onSinksReceived( - eq(UNSUPPORTED_SOURCE), same(mProvider), eq(new ArrayList<MediaSink>())); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnSessionClosedNoClientRecord() { - setUpMediaRouter(mock(MediaRouter.class)); - - CastSession mockSession = mock(CastSession.class); - mProvider.onSessionStarted(mockSession); - - MediaRoute route = new MediaRoute("sink", SUPPORTED_SOURCE, ""); - mProvider.addRoute(route, "", -1); - mProvider.onSessionEnded(); - - verify(mMockManager).onRouteTerminated(route.id); - } - - @Test - @Feature({"MediaRouter"}) - public void testCloseRouteWithNoSession() { - setUpMediaRouter(mock(MediaRouter.class)); - - MediaRoute route = new MediaRoute("sink", SUPPORTED_SOURCE, ""); - mProvider.addRoute(route, "", -1); - mProvider.closeRoute(route.id); - - verify(mMockManager).onRouteTerminated(route.id); - } - - @Test - @Feature({"MediaRouter"}) - public void testAutoJoinWithSameOrigin() { - setUpMediaRouter(mock(MediaRouter.class)); - - CastSession mockSession = mock(CastSession.class); - when(mockSession.getSinkId()).thenReturn("sinkId"); - when(mockSession.getSourceId()).thenReturn(SUPPORTED_AUTOJOIN_SOURCE); - - MediaRoute route = new MediaRoute("sinkId", SUPPORTED_AUTOJOIN_SOURCE, "presentationId"); - mProvider.addRoute(route, "https://example.com", 1); - mProvider.onSessionStarted(mockSession); - mProvider.joinRoute(SUPPORTED_AUTOJOIN_SOURCE, "auto-join", "https://example.com", 1, -1); - - verify(mMockManager) - .onRouteCreated("route:auto-join/sinkId/" + SUPPORTED_AUTOJOIN_SOURCE, "sinkId", -1, - mProvider, false); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoAutoJoinWithOneUniqueOrigin() { - setUpMediaRouter(mock(MediaRouter.class)); - - CastSession mockSession = mock(CastSession.class); - when(mockSession.getSinkId()).thenReturn("sinkId"); - when(mockSession.getSourceId()).thenReturn(SUPPORTED_AUTOJOIN_SOURCE); - - MediaRoute route = new MediaRoute("sinkId", SUPPORTED_AUTOJOIN_SOURCE, "presentationId"); - mProvider.addRoute(route, "https://example.com", 1); - mProvider.onSessionStarted(mockSession); - mProvider.joinRoute(SUPPORTED_AUTOJOIN_SOURCE, "auto-join", "", 1, -1); - - verify(mMockManager).onRouteRequestError("No matching route", -1); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoAutoJoinWithTwoUniqueOrigins() { - setUpMediaRouter(mock(MediaRouter.class)); - - CastSession mockSession = mock(CastSession.class); - when(mockSession.getSinkId()).thenReturn("sinkId"); - when(mockSession.getSourceId()).thenReturn(SUPPORTED_AUTOJOIN_SOURCE); - - MediaRoute route = new MediaRoute("sinkId", SUPPORTED_AUTOJOIN_SOURCE, "presentationId"); - mProvider.addRoute(route, "", 1); - mProvider.onSessionStarted(mockSession); - mProvider.joinRoute(SUPPORTED_AUTOJOIN_SOURCE, "auto-join", "", 1, -1); - - verify(mMockManager).onRouteRequestError("No matching route", -1); - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaSourceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaSourceTest.java deleted file mode 100644 index 4d36d68..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaSourceTest.java +++ /dev/null
@@ -1,161 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; - -/** - * Robolectric tests for {@link CastMediaSource} class. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class CastMediaSourceTest { - @Test - @Feature({"MediaRouter"}) - public void testCorrectSourceId() { - final String sourceId = "https://example.com/path?query" - + "#__castAppId__=ABCD1234(video_out,audio_out)" - + "/__castClientId__=1234567890" - + "/__castAutoJoinPolicy__=tab_and_origin_scoped"; - - CastMediaSource source = CastMediaSource.from(sourceId); - assertNotNull(source); - assertEquals("ABCD1234", source.getApplicationId()); - - assertNotNull(source.getCapabilities()); - assertEquals(2, source.getCapabilities().length); - assertEquals("video_out", source.getCapabilities()[0]); - assertEquals("audio_out", source.getCapabilities()[1]); - - assertEquals("1234567890", source.getClientId()); - assertEquals("tab_and_origin_scoped", source.getAutoJoinPolicy()); - - assertEquals(sourceId, source.getSourceId()); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoFragment() { - assertNull(CastMediaSource.from("https://example.com/path?query")); - } - - @Test - @Feature({"MediaRouter"}) - public void testEmptyFragment() { - assertNull(CastMediaSource.from("https://example.com/path?query#")); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoAppId() { - assertNull(CastMediaSource.from("https://example.com/path?query#fragment")); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoValidAppId() { - // Invalid app id needs to indicate no availability so {@link CastMediaSource} needs to be - // created. - CastMediaSource empty = - CastMediaSource.from("https://example.com/path?query#__castAppId__="); - assertNotNull(empty); - assertEquals("", empty.getApplicationId()); - - CastMediaSource invalid = - CastMediaSource.from("https://example.com/path?query#__castAppId__=INVALID-APP-ID"); - assertNotNull(invalid); - assertEquals("INVALID-APP-ID", invalid.getApplicationId()); - } - - @Test - @Feature({"MediaRouter"}) - public void testNoCapabilitiesSuffix() { - assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A(")); - } - - @Test - @Feature({"MediaRouter"}) - public void testCapabilitiesEmpty() { - assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A()")); - } - - @Test - @Feature({"MediaRouter"}) - public void testInvalidCapability() { - assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A(a)")); - assertNull( - CastMediaSource.from("https://example.com/path?query#__castAppId__=A(video_in,b)")); - } - - @Test - @Feature({"MediaRouter"}) - public void testInvalidAutoJoinPolicy() { - assertNull(CastMediaSource.from("https://example.com/path?query#__castAppId__=A" - + "/__castAutoJoinPolicy__=invalidPolicy")); - } - - @Test - @Feature({"MediaRouter"}) - public void testOptionalParameters() { - CastMediaSource source = - CastMediaSource.from("https://example.com/path?query#__castAppId__=A"); - assertNotNull(source); - assertEquals("A", source.getApplicationId()); - - assertNull(source.getCapabilities()); - assertNull(source.getClientId()); - assertEquals("tab_and_origin_scoped", source.getAutoJoinPolicy()); - } - - @Test - @Feature({"MediaRouter"}) - public void testBasicCastPresentationUrl() { - CastMediaSource source = CastMediaSource.from("cast:ABCD1234"); - assertNotNull(source); - assertEquals("ABCD1234", source.getApplicationId()); - assertNull(source.getCapabilities()); - assertNull(source.getClientId()); - assertEquals("tab_and_origin_scoped", source.getAutoJoinPolicy()); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastPresentationUrlWithParameters() { - CastMediaSource source = CastMediaSource.from("cast:ABCD1234?clientId=1234" - + "&capabilities=video_out,audio_out" - + "&autoJoinPolicy=tab_and_origin_scoped"); - assertNotNull(source); - assertEquals("ABCD1234", source.getApplicationId()); - assertNotNull(source.getCapabilities()); - assertEquals(2, source.getCapabilities().length); - assertEquals("video_out", source.getCapabilities()[0]); - assertEquals("audio_out", source.getCapabilities()[1]); - assertEquals("1234", source.getClientId()); - assertEquals("tab_and_origin_scoped", source.getAutoJoinPolicy()); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastPresentationUrlInvalidCapability() { - assertNull(CastMediaSource.from("cast:ABCD1234?clientId=1234" - + "&capabilities=invalidCapability")); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastPresentationUrlInvalidAutoJoinPolicy() { - assertNull(CastMediaSource.from("cast:ABCD1234?clientId=1234" - + "&autoJoinPolicy=invalidPolicy")); - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java deleted file mode 100644 index 81467be..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java +++ /dev/null
@@ -1,909 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLog; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.media.router.CastSessionUtil; -import org.chromium.chrome.browser.media.router.ClientRecord; -import org.chromium.chrome.browser.media.router.JSONTestUtils.JSONObjectLike; -import org.chromium.chrome.browser.media.router.JSONTestUtils.JSONStringLike; -import org.chromium.chrome.browser.media.router.cast.CastMessageHandler.RequestRecord; - -import java.util.ArrayDeque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Robolectric tests for CastSession. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class CastMessageHandlerTest { - private static final String TAG = "MediaRouter"; - - private static final String SESSION_ID = "SESSION_ID"; - private static final String INVALID_SESSION_ID = "INVALID_SESSION_ID"; - private static final String CLIENT_ID1 = "00000000000000001"; - private static final String CLIENT_ID2 = "00000000000000002"; - private static final String INVALID_CLIENT_ID = "xxxxxxxxxxxxxxxxx"; - private static final String NAMESPACE1 = "namespace1"; - private static final String NAMESPACE2 = "namespace2"; - private static final String MEDIA_NAMESPACE = CastSessionUtil.MEDIA_NAMESPACE; - private static final int SEQUENCE_NUMBER1 = 1; - private static final int SEQUENCE_NUMBER2 = 2; - private static final int REQUEST_ID1 = 1; - private static final int REQUEST_ID2 = 2; - private static final int INVALID_SEQUENCE_NUMBER = - CastMessageHandler.INVALID_SEQUENCE_NUMBER; - private CastMediaRouteProvider mRouteProvider; - private CastSession mSession; - private CastMessageHandler mMessageHandler; - private int mNumStopApplicationCalled; - - private interface CheckedRunnable { void run() throws Exception; } - - @Before - public void setUp() { - ShadowLog.stream = System.out; - mRouteProvider = mock(CastMediaRouteProvider.class); - mSession = mock(CastSession.class); - doReturn(SESSION_ID).when(mSession).getSessionId(); - doReturn(true).when(mSession).sendStringCastMessage( - anyString(), anyString(), anyString(), anyInt()); - mMessageHandler = spy(new CastMessageHandler(mRouteProvider)); - mMessageHandler.onSessionCreated(mSession); - final Set<String> clientIds = new HashSet<String>(); - clientIds.add(CLIENT_ID1); - clientIds.add(CLIENT_ID2); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - return clientIds; - } - }) - .when(mRouteProvider) - .getClients(); - doNothing().when(mRouteProvider).onMessage(anyString(), anyString()); - } - - void setUpForAppMessageTest() throws JSONException { - Set<String> namespaces = new HashSet<String>(); - namespaces.add(NAMESPACE1); - doReturn(namespaces).when(mSession).getNamespaces(); - doReturn(true).when(mMessageHandler) - .sendJsonCastMessage(any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - - @Test - @Feature({"MediaRouter"}) - public void testOnSessionCreated() { - Map<String, ClientRecord> clientRecords = new HashMap<String, ClientRecord>(); - clientRecords.put(CLIENT_ID1, new ClientRecord( - "DONTCARE", CLIENT_ID1, "DONTCARE", "DONTCARE", "DONTCARE", 0)); - clientRecords.put(CLIENT_ID2, new ClientRecord( - "DONTCARE", CLIENT_ID2, "DONTCARE", "DONTCARE", "DONTCARE", 0)); - clientRecords.get(CLIENT_ID1).isConnected = true; - doReturn(clientRecords).when(mRouteProvider).getClientRecords(); - // The call in setUp() actually only sets the session. This call will notify the clients - // that have sent "client_connect" - mMessageHandler.onSessionCreated(mSession); - verify(mSession).onClientConnected(CLIENT_ID1); - verify(mSession, never()).onClientConnected(CLIENT_ID2); - } - - @Test - @Feature({"MediaRouter"}) - public void testHandleSessionMessageOfV2MessageType() throws JSONException { - doReturn(true).when(mMessageHandler).handleCastV2Message(any(JSONObject.class)); - - JSONObject message = new JSONObject(); - message.put("type", "v2_message"); - assertTrue(mMessageHandler.handleSessionMessage(message)); - verify(mMessageHandler).handleCastV2Message(argThat(new JSONObjectLike(message))); - } - - @Test - @Feature({"MediaRouter"}) - public void testHandleSessionMessageOfAppMessageType() throws JSONException { - doReturn(true).when(mMessageHandler).handleAppMessage(any(JSONObject.class)); - - JSONObject message = new JSONObject(); - message.put("type", "app_message"); - assertTrue(mMessageHandler.handleSessionMessage(message)); - verify(mMessageHandler).handleAppMessage(argThat(new JSONObjectLike(message))); - } - - @Test - @Feature({"MediaRouter"}) - public void testHandleSessionMessageOfUnsupportedType() throws JSONException { - doReturn(true).when(mMessageHandler).handleCastV2Message(any(JSONObject.class)); - - JSONObject message = new JSONObject(); - message.put("type", "unsupported"); - assertFalse(mMessageHandler.handleSessionMessage(message)); - verify(mMessageHandler, never()).handleCastV2Message(any(JSONObject.class)); - verify(mMessageHandler, never()).handleAppMessage(any(JSONObject.class)); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageWithWrongTypeInnerMessage() throws JSONException { - JSONObject innerMessage = new JSONObject() - .put("type", "STOP"); - final JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - // Replace the inner JSON message with string. - message.put("message", "wrong type inner message"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleCastV2Message(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).handleStopMessage(anyString(), anyInt()); - verify(mSession, never()).handleVolumeMessage(any(JSONObject.class), anyString(), anyInt()); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageOfStopType() throws JSONException { - JSONObject innerMessage = new JSONObject() - .put("type", "STOP"); - JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - assertTrue(mMessageHandler.handleCastV2Message(message)); - verify(mMessageHandler).handleStopMessage(eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageofSetVolumeTypeShouldWait() throws JSONException { - doReturn(new CastSession.HandleVolumeMessageResult(true, true)) - .when(mSession).handleVolumeMessage(any(JSONObject.class), anyString(), anyInt()); - JSONObject innerMessage = new JSONObject() - .put("type", "SET_VOLUME") - .put("volume", new JSONObject() - .put("level", (double) 1) - .put("muted", false)); - JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - assertTrue(mMessageHandler.handleCastV2Message(message)); - JSONObject volumeMessage = innerMessage.getJSONObject("volume"); - verify(mSession).handleVolumeMessage( - argThat(new JSONObjectLike(innerMessage.getJSONObject("volume"))), - eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - assertEquals(mMessageHandler.getVolumeRequestsForTest().size(), 1); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageofSetVolumeTypeShouldNotWait() throws JSONException { - doReturn(new CastSession.HandleVolumeMessageResult(true, false)) - .when(mSession).handleVolumeMessage(any(JSONObject.class), anyString(), anyInt()); - JSONObject innerMessage = new JSONObject() - .put("type", "SET_VOLUME") - .put("volume", new JSONObject() - .put("level", (double) 1) - .put("muted", false)); - JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - assertTrue(mMessageHandler.handleCastV2Message(message)); - JSONObject volumeMessage = innerMessage.getJSONObject("volume"); - verify(mSession).handleVolumeMessage( - argThat(new JSONObjectLike(innerMessage.getJSONObject("volume"))), - eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - assertEquals(mMessageHandler.getVolumeRequestsForTest().size(), 0); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageofSetVolumeTypeWithNullVolumeMessage() throws JSONException { - JSONObject innerMessage = new JSONObject() - .put("type", "SET_VOLUME"); - final JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleCastV2Message(message); - } - }, JSONException.class); - verify(mSession, never()).handleVolumeMessage(any(JSONObject.class), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageofSetVolumeTypeWithWrongTypeVolumeMessage() throws JSONException { - JSONObject innerMessage = new JSONObject() - .put("type", "SET_VOLUME") - .put("volume", "wrong type volume message"); - final JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleCastV2Message(message); - } - }, JSONException.class); - verify(mSession, never()).handleVolumeMessage(any(JSONObject.class), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageOfMediaMessageType() throws JSONException { - doReturn(true).when(mMessageHandler).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - for (String messageType : CastMessageHandler.getMediaMessageTypesForTest()) { - // TODO(zqzhang): SET_VOLUME and STOP should not reach here? - if ("MEDIA_SET_VOLUME".equals(messageType) || "STOP_MEDIA".equals(messageType)) { - continue; - } - JSONObject innerMessage = new JSONObject().put("type", messageType); - JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - assertTrue(mMessageHandler.handleCastV2Message(message)); - - JSONObject expected = new JSONObject(); - if (CastMessageHandler.getMediaOverloadedMessageTypesForTest() - .containsKey(messageType)) { - expected.put("type", CastMessageHandler.getMediaOverloadedMessageTypesForTest() - .get(messageType)); - } else { - expected.put("type", messageType); - } - verify(mMessageHandler) - .sendJsonCastMessage(argThat(new JSONObjectLike(expected)), - eq(CastSessionUtil.MEDIA_NAMESPACE), eq(CLIENT_ID1), - eq(SEQUENCE_NUMBER1)); - } - } - - @Test - @Feature({"MediaRouter"}) - public void testCastV2MessageWithNullSequenceNumber() throws JSONException { - JSONObject innerMessage = new JSONObject() - .put("type", "STOP"); - JSONObject message = buildCastV2Message(CLIENT_ID1, innerMessage); - message.remove("sequenceNumber"); - assertTrue(mMessageHandler.handleCastV2Message(message)); - verify(mMessageHandler).handleStopMessage(eq(CLIENT_ID1), eq(INVALID_SEQUENCE_NUMBER)); - } - - @Test - @Feature({"MediaRouter"}) - public void testHandleStopMessage() throws JSONException { - assertEquals(0, mMessageHandler.getStopRequestsForTest().size()); - mMessageHandler.handleStopMessage(CLIENT_ID1, SEQUENCE_NUMBER1); - assertEquals(1, mMessageHandler.getStopRequestsForTest().get(CLIENT_ID1).size(), 1); - verify(mSession).stopApplication(); - mMessageHandler.handleStopMessage(CLIENT_ID1, SEQUENCE_NUMBER2); - assertEquals(2, mMessageHandler.getStopRequestsForTest().get(CLIENT_ID1).size(), 2); - verify(mSession, times(2)).stopApplication(); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithExistingNamespace() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - assertTrue(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler).sendJsonCastMessage( - argThat(new JSONObjectLike(actualMessage)), eq(NAMESPACE1), - eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNonexistingNamespace() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE2, actualMessage); - assertFalse(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithoutSequenceNumber() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.remove("sequenceNumber"); - assertTrue(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler).sendJsonCastMessage( - argThat(new JSONObjectLike(actualMessage)), eq(NAMESPACE1), - eq(CLIENT_ID1), eq(INVALID_SEQUENCE_NUMBER)); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNullSessionId() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.getJSONObject("message").remove("sessionId"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithWrongSessionId() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.getJSONObject("message").put("sessionId", INVALID_SESSION_ID); - assertFalse(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNullActualMessage() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.getJSONObject("message").remove("message"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithStringMessage() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.getJSONObject("message").put("message", "string message"); - assertTrue(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - verify(mSession).sendStringCastMessage( - eq("string message"), eq(NAMESPACE1), eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNullAppMessage() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.remove("message"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithEmptyAppMessage() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.put("message", new JSONObject()); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithWrongTypeAppMessage() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.put("message", "wrong type app message"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNullClient() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - final JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.remove("clientId"); - expectException(new CheckedRunnable() { - @Override - public void run() throws Exception { - mMessageHandler.handleAppMessage(message); - } - }, JSONException.class); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testAppMessageWithNonexistingClient() throws JSONException { - setUpForAppMessageTest(); - - JSONObject actualMessage = buildActualAppMessage(); - JSONObject message = buildAppMessage(CLIENT_ID1, NAMESPACE1, actualMessage); - message.put("clientId", INVALID_CLIENT_ID); - assertFalse(mMessageHandler.handleAppMessage(message)); - verify(mMessageHandler, never()).sendJsonCastMessage( - any(JSONObject.class), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testSendJsonCastMessage() throws JSONException { - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - JSONObject message = buildJsonCastMessage("message"); - assertTrue(mMessageHandler.sendJsonCastMessage( - message, NAMESPACE1, CLIENT_ID1, SEQUENCE_NUMBER1)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - verify(mSession).sendStringCastMessage( - argThat(new JSONStringLike(message)), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testSendJsonCastMessageWhenApiClientInvalid() throws JSONException { - doReturn(true).when(mSession).isApiClientInvalid(); - - JSONObject message = buildJsonCastMessage("message"); - assertFalse(mMessageHandler.sendJsonCastMessage( - message, NAMESPACE1, CLIENT_ID1, SEQUENCE_NUMBER1)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - verify(mSession, never()).sendStringCastMessage( - anyString(), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testSendJsonCastMessageWithInvalidSequenceNumber() throws JSONException { - JSONObject message = buildJsonCastMessage("message"); - assertTrue(mMessageHandler.sendJsonCastMessage( - message, NAMESPACE1, CLIENT_ID1, INVALID_SEQUENCE_NUMBER)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - verify(mSession).sendStringCastMessage( - argThat(new JSONStringLike(message)), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testSendJsonCastMessageWithNullRequestId() throws JSONException { - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - JSONObject message = buildJsonCastMessage("message"); - message.remove("requestId"); - assertTrue(mMessageHandler.sendJsonCastMessage( - message, NAMESPACE1, CLIENT_ID1, SEQUENCE_NUMBER1)); - assertTrue(message.has("requestId")); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - verify(mSession).sendStringCastMessage( - argThat(new JSONStringLike(message)), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMessageReceivedWithExistingRequestId() throws JSONException { - doNothing().when(mMessageHandler).onAppMessage( - anyString(), anyString(), any(RequestRecord.class)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.getRequestsForTest().append( - REQUEST_ID1, request); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - JSONObject message = new JSONObject(); - message.put("requestId", REQUEST_ID1); - mMessageHandler.onMessageReceived(NAMESPACE1, message.toString()); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - verify(mMessageHandler).onAppMessage(eq(message.toString()), eq(NAMESPACE1), eq(request)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMessageReceivedWithNonexistingRequestId() throws JSONException { - doNothing().when(mMessageHandler).onAppMessage( - anyString(), anyString(), any(RequestRecord.class)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.getRequestsForTest().append( - REQUEST_ID1, request); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - JSONObject message = new JSONObject(); - message.put("requestId", REQUEST_ID2); - mMessageHandler.onMessageReceived(NAMESPACE1, message.toString()); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - verify(mMessageHandler).onAppMessage( - eq(message.toString()), eq(NAMESPACE1), (RequestRecord) isNull()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMessageReceivedWithoutRequestId() throws JSONException { - doNothing().when(mMessageHandler).onAppMessage( - anyString(), anyString(), any(RequestRecord.class)); - assertEquals(mMessageHandler.getRequestsForTest().size(), 0); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.getRequestsForTest().append( - REQUEST_ID1, request); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - JSONObject message = new JSONObject(); - mMessageHandler.onMessageReceived(NAMESPACE1, message.toString()); - assertEquals(mMessageHandler.getRequestsForTest().size(), 1); - verify(mMessageHandler).onAppMessage( - eq(message.toString()), eq(NAMESPACE1), (RequestRecord) isNull()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMessageReceivedOfMediaNamespace() throws JSONException { - doNothing().when(mMessageHandler).onMediaMessage(anyString(), any(RequestRecord.class)); - mMessageHandler.onMessageReceived(MEDIA_NAMESPACE, "anymessage"); - verify(mMessageHandler).onMediaMessage(eq("anymessage"), (RequestRecord) isNull()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMediaMessageOfMediaStatusTypeWithRequestRecord() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - doReturn(true).when(mMessageHandler).isMediaStatusMessage(anyString()); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.onMediaMessage("anymessage", request); - verify(mMessageHandler, never()).sendClientMessageTo( - eq(CLIENT_ID1), eq("v2_message"), eq("anymessage"), eq(INVALID_SEQUENCE_NUMBER)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID2), eq("v2_message"), eq("anymessage"), eq(INVALID_SEQUENCE_NUMBER)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("v2_message"), eq("anymessage"), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMediaMessageOfMediaStatusTypeWithNullRequestRecord() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - doReturn(true).when(mMessageHandler).isMediaStatusMessage(anyString()); - mMessageHandler.onMediaMessage("anymessage", null); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("v2_message"), eq("anymessage"), eq(INVALID_SEQUENCE_NUMBER)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID2), eq("v2_message"), eq("anymessage"), eq(INVALID_SEQUENCE_NUMBER)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMediaMessageOfNonMediaStatusTypeWithRequestRecord() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - doReturn(false).when(mMessageHandler).isMediaStatusMessage(anyString()); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.onMediaMessage("anymessage", request); - verify(mMessageHandler, never()).sendClientMessageTo( - anyString(), anyString(), anyString(), eq(INVALID_SEQUENCE_NUMBER)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("v2_message"), eq("anymessage"), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnMediaMessageOfNonMediaStatusTypeWithNullRequestRecord() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - doReturn(false).when(mMessageHandler).isMediaStatusMessage(anyString()); - mMessageHandler.onMediaMessage("anymessage", null); - verify(mMessageHandler, never()).sendClientMessageTo( - anyString(), anyString(), anyString(), eq(INVALID_SEQUENCE_NUMBER)); - verify(mMessageHandler, never()).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnAppMessageWithRequestRecord() throws JSONException { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - RequestRecord request = new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1); - mMessageHandler.onAppMessage("anyMessage", NAMESPACE1, request); - JSONObject expected = new JSONObject(); - expected.put("sessionId", SESSION_ID); - expected.put("namespaceName", NAMESPACE1); - expected.put("message", "anyMessage"); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("app_message"), - argThat(new JSONStringLike(expected)), eq(SEQUENCE_NUMBER1)); - verify(mMessageHandler, never()).broadcastClientMessage(anyString(), anyString()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnAppMessageWithNullRequestRecord() throws JSONException { - doNothing().when(mMessageHandler).broadcastClientMessage(anyString(), anyString()); - mMessageHandler.onAppMessage("anyMessage", NAMESPACE1, null); - JSONObject expected = new JSONObject(); - expected.put("sessionId", SESSION_ID); - expected.put("namespaceName", NAMESPACE1); - expected.put("message", "anyMessage"); - verify(mMessageHandler, never()).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - verify(mMessageHandler).broadcastClientMessage( - eq("app_message"), argThat(new JSONStringLike(expected))); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnApplicationStopped() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - assertEquals(0, mMessageHandler.getStopRequestsForTest().size()); - mMessageHandler.getStopRequestsForTest().put(CLIENT_ID1, new ArrayDeque<Integer>()); - mMessageHandler.getStopRequestsForTest().get(CLIENT_ID1).add(SEQUENCE_NUMBER1); - mMessageHandler.getStopRequestsForTest().get(CLIENT_ID1).add(SEQUENCE_NUMBER2); - assertEquals(1, mMessageHandler.getStopRequestsForTest().size()); - assertEquals(2, mMessageHandler.getStopRequestsForTest().get(CLIENT_ID1).size()); - - mMessageHandler.onApplicationStopped(); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("remove_session"), eq(SESSION_ID), eq(SEQUENCE_NUMBER1)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("remove_session"), eq(SESSION_ID), eq(SEQUENCE_NUMBER2)); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID2), eq("remove_session"), eq(SESSION_ID), eq(INVALID_SEQUENCE_NUMBER)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnVolumeChanged() { - doNothing().when(mMessageHandler).onVolumeChanged(anyString(), anyInt()); - assertEquals(0, mMessageHandler.getVolumeRequestsForTest().size()); - mMessageHandler.getVolumeRequestsForTest() - .add(new RequestRecord(CLIENT_ID1, SEQUENCE_NUMBER1)); - assertEquals(1, mMessageHandler.getVolumeRequestsForTest().size()); - - mMessageHandler.onVolumeChanged(); - verify(mMessageHandler).onVolumeChanged(CLIENT_ID1, SEQUENCE_NUMBER1); - assertEquals(0, mMessageHandler.getVolumeRequestsForTest().size()); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnVolumeChangedWithEmptyVolumeRequests() { - mMessageHandler.onVolumeChanged(); - verify(mMessageHandler, never()).onVolumeChanged(eq(CLIENT_ID1), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnVolumeChangedForClient() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - mMessageHandler.onVolumeChanged(CLIENT_ID1, SEQUENCE_NUMBER1); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("v2_message"), (String) isNull(), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testOnAppMessageSent() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - mMessageHandler.onAppMessageSent(CLIENT_ID1, SEQUENCE_NUMBER1); - verify(mMessageHandler).sendClientMessageTo( - eq(CLIENT_ID1), eq("app_message"), (String) isNull(), eq(SEQUENCE_NUMBER1)); - } - - @Test - @Feature({"MediaRouter"}) - public void testSendClientMessageTo() throws JSONException { - String message = "discarded_message1"; - doReturn(message).when(mMessageHandler) - .buildInternalMessage(anyString(), anyString(), anyString(), anyInt()); - mMessageHandler.sendClientMessageTo( - CLIENT_ID1, "anytype", "discarded_message2", SEQUENCE_NUMBER1); - - verify(mRouteProvider).onMessage( - eq(CLIENT_ID1), eq(message)); - } - - @Test - @Feature({"MediaRouter"}) - public void testBroadcastClientMessage() { - doNothing().when(mMessageHandler).sendClientMessageTo( - anyString(), anyString(), anyString(), anyInt()); - mMessageHandler.broadcastClientMessage("anytype", "anymessage"); - for (String clientId : mRouteProvider.getClients()) { - verify(mMessageHandler).sendClientMessageTo( - eq(clientId), eq("anytype"), eq("anymessage"), eq(INVALID_SEQUENCE_NUMBER)); - } - } - - @Test - @Feature({"MediaRouter"}) - public void testBuildInternalMessageWithNullMessage() throws JSONException { - String message = mMessageHandler.buildInternalMessage( - "anytype", null, CLIENT_ID1, SEQUENCE_NUMBER1); - JSONObject expected = new JSONObject(); - expected.put("type", "anytype"); - expected.put("sequenceNumber", SEQUENCE_NUMBER1); - expected.put("timeoutMillis", 0); - expected.put("clientId", CLIENT_ID1); - expected.put("message", null); - - assertTrue("\nexpected: " + expected.toString() + ",\n actual: " + message.toString(), - new JSONObjectLike(expected).matches(new JSONObject(message))); - } - - @Test - @Feature({"MediaRouter"}) - public void testBuildInternalMessageOfRemoveSessionType() throws JSONException { - String message = mMessageHandler.buildInternalMessage( - "remove_session", SESSION_ID, CLIENT_ID1, SEQUENCE_NUMBER1); - JSONObject expected = new JSONObject(); - expected.put("type", "remove_session"); - expected.put("sequenceNumber", SEQUENCE_NUMBER1); - expected.put("timeoutMillis", 0); - expected.put("clientId", CLIENT_ID1); - expected.put("message", SESSION_ID); - - assertTrue("\nexpected: " + expected.toString() + ",\n actual: " + message.toString(), - new JSONObjectLike(expected).matches(new JSONObject(message))); - } - - @Test - @Feature({"MediaRouter"}) - public void testBuildInternalMessageOfDisconnectSessionType() throws JSONException { - String message = mMessageHandler.buildInternalMessage( - "disconnect_session", SESSION_ID, CLIENT_ID1, SEQUENCE_NUMBER1); - JSONObject expected = new JSONObject(); - expected.put("type", "disconnect_session"); - expected.put("sequenceNumber", SEQUENCE_NUMBER1); - expected.put("timeoutMillis", 0); - expected.put("clientId", CLIENT_ID1); - expected.put("message", SESSION_ID); - - assertTrue("\nexpected: " + expected.toString() + ",\n actual: " + message.toString(), - new JSONObjectLike(expected).matches(new JSONObject(message))); - } - - @Test - @Feature({"MediaRouter"}) - public void testBuildInternalMessageWithInnerMessage() throws JSONException { - JSONObject innerMessage = buildSessionMessage(SESSION_ID); - String message = mMessageHandler.buildInternalMessage( - "anytype", innerMessage.toString(), CLIENT_ID1, SEQUENCE_NUMBER1); - JSONObject expected = new JSONObject(); - expected.put("type", "anytype"); - expected.put("sequenceNumber", SEQUENCE_NUMBER1); - expected.put("timeoutMillis", 0); - expected.put("clientId", CLIENT_ID1); - expected.put("message", innerMessage); - - assertTrue("\nexpected: " + expected.toString() + ",\n actual: " + message.toString(), - new JSONObjectLike(expected).matches(new JSONObject(message))); - } - - JSONObject buildCastV2Message(String clientId, JSONObject innerMessage) throws JSONException { - JSONObject message = new JSONObject(); - message.put("type", "v2_message"); - message.put("message", innerMessage); - message.put("sequenceNumber", SEQUENCE_NUMBER1); - message.put("timeoutMillis", 0); - message.put("clientId", clientId); - - return message; - } - - JSONObject buildAppMessage(String clientId, String namespace, Object actualMessage) - throws JSONException { - JSONObject innerMessage = new JSONObject(); - innerMessage.put("sessionId", mSession.getSessionId()); - innerMessage.put("namespaceName", namespace); - innerMessage.put("message", actualMessage); - - JSONObject message = new JSONObject(); - message.put("type", "app_message"); - message.put("message", innerMessage); - message.put("sequenceNumber", SEQUENCE_NUMBER1); - message.put("timeoutMillis", 0); - message.put("clientId", clientId); - - return message; - } - - JSONObject buildActualAppMessage() throws JSONException { - JSONObject message = new JSONObject(); - message.put("type", "actual app message type"); - - return message; - } - - JSONObject buildSessionMessage(String sessionId) throws JSONException { - JSONObject message = new JSONObject(); - message.put("sessionId", sessionId); - - return message; - } - - void expectException(CheckedRunnable r, Class exceptionClass) { - boolean caughtException = false; - try { - r.run(); - } catch (Exception e) { - if (e.getClass() == exceptionClass) caughtException = true; - } - assertTrue(caughtException); - } - - JSONObject buildJsonCastMessage(String message) throws JSONException { - JSONObject jsonMessage = new JSONObject(); - jsonMessage.put("requestId", REQUEST_ID1); - jsonMessage.put("message", message); - return jsonMessage; - } -}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManagerTest.java deleted file mode 100644 index 157c217..0000000 --- a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManagerTest.java +++ /dev/null
@@ -1,208 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.media.router.cast; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; -import org.chromium.chrome.browser.media.router.cast.ChromeCastSessionManager.CastSessionLaunchRequest; -import org.chromium.chrome.browser.media.router.cast.ChromeCastSessionManager.CastSessionManagerListener; - -/** - * Robolectric tests for {@link CastMediaSource} class. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class ChromeCastSessionManagerTest { - private CastSessionManagerListener mListener; - private CastSessionLaunchRequest mRequest; - private CastSession mSession; - private ChromeCastSessionManager mManager; - - // Mocks to be used in scenarios where multiple sessions are launched. - private CastSessionManagerListener mAltListener; - private CastSessionLaunchRequest mAltRequest; - private CastSession mAltSession; - - @Before - public void setup() { - mListener = mock(CastSessionManagerListener.class); - mRequest = mock(CastSessionLaunchRequest.class); - mSession = mock(CastSession.class); - - doReturn(mListener).when(mRequest).getSessionListener(); - - mManager = ChromeCastSessionManager.get(); - } - - private void setupAlternateMocks() { - mAltListener = mock(CastSessionManagerListener.class); - mAltRequest = mock(CastSessionLaunchRequest.class); - mAltSession = mock(CastSession.class); - - doReturn(mAltListener).when(mAltRequest).getSessionListener(); - } - - @After - public void cleanup() { - ChromeCastSessionManager.resetInstanceForTesting(); - } - - private void setupLaunch( - CastSessionLaunchRequest request, CastSession session, boolean success) { - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - if (success) - mManager.onSessionStarted(session); - else - mManager.onSessionStartFailed(); - - return null; - } - }) - .when(request) - .start(any()); - } - - @Test - @Feature({"MediaRouter"}) - public void testLaunchingRequest() { - mManager.requestSessionLaunch(mRequest); - verify(mRequest).start(any()); - verify(mListener).onSessionStarting(mRequest); - } - - @Test - @Feature({"MediaRouter"}) - public void testSessionRequestSucceeded() { - // Setup a successful launch. - setupLaunch(mRequest, mSession, true); - - mManager.requestSessionLaunch(mRequest); - verify(mListener).onSessionStarted(mSession); - } - - @Test - @Feature({"MediaRouter"}) - public void testSessionRequestFailed() { - // Setup a failed launch. - setupLaunch(mRequest, null, false); - - mManager.requestSessionLaunch(mRequest); - verify(mListener).onSessionStartFailed(); - } - - @Test - @Feature({"MediaRouter"}) - public void testNewRequestWaitOnSessionClose() { - // Make sure |mManager| currently has a session. - setupAlternateMocks(); - setupLaunch(mAltRequest, mAltSession, true); - mManager.requestSessionLaunch(mAltRequest); - - // Simulate the session taking a while to stop. - doNothing().when(mAltSession).stopApplication(); - - // Launching a new request should stop the current session, and wait until it has has - // stopped before launching the request. - mManager.requestSessionLaunch(mRequest); - verify(mAltSession).stopApplication(); - verify(mRequest, never()).start(any()); - } - - @Test - @Feature({"MediaRouter"}) - public void testNewSessionStopsOldOnes() { - // Make sure |mManager| currently has a session. - setupAlternateMocks(); - setupLaunch(mAltRequest, mAltSession, true); - mManager.requestSessionLaunch(mAltRequest); - - // Setup the current session to successfuly stop. - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - mManager.onSessionEnded(); - return null; - } - }) - .when(mAltSession) - .stopApplication(); - - // The old observer should be notified of a session closing. - // The new request should be launched - mManager.requestSessionLaunch(mRequest); - verify(mAltListener).onSessionEnded(); - verify(mRequest).start(any()); - } - - // Makes sure we can override pending requests while waiting for an old - // session to close. - @Test - @Feature({"MediaRouter"}) - public void testNewRequestOverwritePendingOnes() { - // Make sure |mManager| currently has a session. - setupAlternateMocks(); - setupLaunch(mAltRequest, mAltSession, true); - mManager.requestSessionLaunch(mAltRequest); - - // Simulate the current session taking a while to stop. - doNothing().when(mAltSession).stopApplication(); - - // Make sure the queued request isn't launched immediately. - CastSessionLaunchRequest middleRequest = mock(CastSessionLaunchRequest.class); - mManager.requestSessionLaunch(middleRequest); - - // Queue up another pending request. - mManager.requestSessionLaunch(mRequest); - - // Simulate |mAltSession| finally closing. - mManager.onSessionEnded(); - - // The first request shouldn't have started. - verify(middleRequest, never()).start(any()); - // The last request should have been started. - verify(mRequest).start(any()); - } - - // Makes sure new requests are dropped when we are waiting for a request to - // complete its launch. - @Test - @Feature({"MediaRouter"}) - public void testRequestAreDroppedWhenLaunching() { - setupAlternateMocks(); - - // Simulate a slow to launch request. - doNothing().when(mAltRequest).start(any()); - mManager.requestSessionLaunch(mAltRequest); - - // Queue up a new request whilst launching. - // Make this new request isn't started. - setupLaunch(mRequest, mSession, true); - mManager.requestSessionLaunch(mRequest); - verify(mRequest, never()).start(any()); - - // Simulate the completion of the slow request. - // Make sure the 2nd request was not launched. - mManager.onSessionStarted(mAltSession); - verify(mRequest, never()).start(any()); - } -}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 5511262..e0f586f 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1543,6 +1543,8 @@ "ssl/ssl_blocking_page_base.cc", "ssl/ssl_blocking_page_base.h", "ssl/ssl_cert_reporter.h", + "ssl/ssl_client_auth_metrics.cc", + "ssl/ssl_client_auth_metrics.h", "ssl/ssl_client_certificate_selector.h", "ssl/ssl_config_service_manager.h", "ssl/ssl_config_service_manager_pref.cc", @@ -3214,8 +3216,12 @@ "apps/app_service/arc_apps_factory.h", "apps/app_service/built_in_chromeos_apps.cc", "apps/app_service/built_in_chromeos_apps.h", + "apps/app_service/crostini_apps.cc", + "apps/app_service/crostini_apps.h", "apps/app_service/extension_apps.cc", "apps/app_service/extension_apps.h", + "apps/app_service/launch_util.cc", + "apps/app_service/launch_util.h", "ash_service_registry.cc", "ash_service_registry.h", "component_updater/cros_component_installer_chromeos.cc",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index 9eadc92c..72040b0 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -163,7 +163,6 @@ &payments::features::kWebPaymentsModifiers, &payments::features::kWebPaymentsSingleAppUiSkip, &language::kExplicitLanguageAsk, - &media::kCafMediaRouterImpl, &ntp_snippets::kArticleSuggestionsFeature, &ntp_snippets::kIncreasedVisibility, &ntp_snippets::kNotificationsFeature,
diff --git a/chrome/browser/apps/app_service/app_service_proxy.cc b/chrome/browser/apps/app_service/app_service_proxy.cc index 056cc1c..f736bcc 100644 --- a/chrome/browser/apps/app_service/app_service_proxy.cc +++ b/chrome/browser/apps/app_service/app_service_proxy.cc
@@ -36,6 +36,7 @@ // responsibility isn't intrinsically part of the AppServiceProxy, but doing // that here, for each such app type, is as good a place as any. built_in_chrome_os_apps_.Initialize(app_service_, profile); + crostini_apps_.Initialize(app_service_, profile); extension_apps_.Initialize(app_service_, profile, apps::mojom::AppType::kExtension); extension_web_apps_.Initialize(app_service_, profile,
diff --git a/chrome/browser/apps/app_service/app_service_proxy.h b/chrome/browser/apps/app_service/app_service_proxy.h index 2efc254..0c8c2667 100644 --- a/chrome/browser/apps/app_service/app_service_proxy.h +++ b/chrome/browser/apps/app_service/app_service_proxy.h
@@ -14,6 +14,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/apps/app_service/built_in_chromeos_apps.h" +#include "chrome/browser/apps/app_service/crostini_apps.h" #include "chrome/browser/apps/app_service/extension_apps.h" #endif // OS_CHROMEOS @@ -69,6 +70,7 @@ #if defined(OS_CHROMEOS) BuiltInChromeOsApps built_in_chrome_os_apps_; + CrostiniApps crostini_apps_; ExtensionApps extension_apps_; ExtensionApps extension_web_apps_; #endif // OS_CHROMEOS
diff --git a/chrome/browser/apps/app_service/app_service_proxy_factory.cc b/chrome/browser/apps/app_service/app_service_proxy_factory.cc index 6bfec56..3e62aba 100644 --- a/chrome/browser/apps/app_service/app_service_proxy_factory.cc +++ b/chrome/browser/apps/app_service/app_service_proxy_factory.cc
@@ -11,6 +11,7 @@ #include "components/keyed_service/content/browser_context_dependency_manager.h" #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" #include "extensions/browser/extension_registry_factory.h" #endif // OS_CHROMEOS @@ -46,6 +47,7 @@ "AppServiceProxy", BrowserContextDependencyManager::GetInstance()) { #if defined(OS_CHROMEOS) + DependsOn(crostini::CrostiniRegistryServiceFactory::GetInstance()); DependsOn(extensions::ExtensionRegistryFactory::GetInstance()); #endif // OS_CHROMEOS }
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc index ac996cf..ed4ddbf 100644 --- a/chrome/browser/apps/app_service/arc_apps.cc +++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -115,18 +115,18 @@ } ArcApps::ArcApps(Profile* profile) - : binding_(this), - profile_(profile), - prefs_(ArcAppListPrefs::Get(profile)), - next_u_key_(1) { - if (!prefs_ || !arc::IsArcAllowedForProfile(profile_)) { + : binding_(this), profile_(profile), prefs_(nullptr), next_u_key_(1) { + if (!arc::IsArcAllowedForProfile(profile_) || + (arc::ArcServiceManager::Get() == nullptr)) { return; } - auto* arc_service_manager = arc::ArcServiceManager::Get(); - if (!arc_service_manager) { + + prefs_ = ArcAppListPrefs::Get(profile); + if (!prefs_) { return; } - ObservePrefs(); + prefs_->AddObserver(this); + prefs_->app_connection_holder()->AddObserver(this); apps::mojom::PublisherPtr publisher; binding_.Bind(mojo::MakeRequest(&publisher)); @@ -144,13 +144,10 @@ void ArcApps::Connect(apps::mojom::SubscriberPtr subscriber, apps::mojom::ConnectOptionsPtr opts) { std::vector<apps::mojom::AppPtr> apps; - if (prefs_) { - for (const auto& app_id : prefs_->GetAppIds()) { - std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = - prefs_->GetApp(app_id); - if (app_info) { - apps.push_back(Convert(app_id, *app_info)); - } + for (const auto& app_id : prefs_->GetAppIds()) { + std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = prefs_->GetApp(app_id); + if (app_info) { + apps.push_back(Convert(app_id, *app_info)); } } subscriber->OnApps(std::move(apps)); @@ -162,7 +159,7 @@ apps::mojom::IconCompression icon_compression, int32_t size_hint_in_dip, LoadIconCallback callback) { - if (prefs_ && !icon_key.is_null() && + if (!icon_key.is_null() && (icon_key->icon_type == apps::mojom::IconType::kArc) && !icon_key->s_key.empty()) { // Treat the Play Store as a special case, loading an icon defined by a @@ -275,18 +272,11 @@ } void ArcApps::OnConnectionReady() { - if (!prefs_) { - prefs_ = ArcAppListPrefs::Get(profile_); - ObservePrefs(); + AppConnectionHolder* app_connection_holder = prefs_->app_connection_holder(); + for (auto& pending : pending_load_icon_calls_) { + std::move(pending).Run(app_connection_holder); } - if (prefs_) { - AppConnectionHolder* app_connection_holder = - prefs_->app_connection_holder(); - for (auto& pending : pending_load_icon_calls_) { - std::move(pending).Run(app_connection_holder); - } - pending_load_icon_calls_.clear(); - } + pending_load_icon_calls_.clear(); } void ArcApps::OnAppRegistered(const std::string& app_id, @@ -340,17 +330,8 @@ } } -void ArcApps::ObservePrefs() { - prefs_->AddObserver(this); - prefs_->app_connection_holder()->AddObserver(this); -} - const base::FilePath ArcApps::GetCachedIconFilePath(const std::string& app_id, int32_t size_hint_in_dip) { - if (!prefs_) { - return base::FilePath(); - } - // TODO(crbug.com/826982): process the app_id argument like the private // GetAppFromAppOrGroupId function and the ArcAppIcon::mapped_app_id_ field // in arc_app_icon.cc? @@ -365,25 +346,23 @@ apps::mojom::IconCompression icon_compression, int32_t size_hint_in_dip, LoadIconCallback callback) { - if (prefs_) { - std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = - prefs_->GetApp(icon_key_s_key); - if (app_info) { - base::OnceCallback<void(apps::ArcApps::AppConnectionHolder*)> pending = - base::BindOnce(&LoadIcon0, icon_compression, - ConvertDipToPx(size_hint_in_dip), - app_info->package_name, app_info->activity, - app_info->icon_resource_id, std::move(callback)); + std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = + prefs_->GetApp(icon_key_s_key); + if (app_info) { + base::OnceCallback<void(apps::ArcApps::AppConnectionHolder*)> pending = + base::BindOnce(&LoadIcon0, icon_compression, + ConvertDipToPx(size_hint_in_dip), app_info->package_name, + app_info->activity, app_info->icon_resource_id, + std::move(callback)); - AppConnectionHolder* app_connection_holder = - prefs_->app_connection_holder(); - if (app_connection_holder->IsConnected()) { - std::move(pending).Run(app_connection_holder); - } else { - pending_load_icon_calls_.push_back(std::move(pending)); - } - return; + AppConnectionHolder* app_connection_holder = + prefs_->app_connection_holder(); + if (app_connection_holder->IsConnected()) { + std::move(pending).Run(app_connection_holder); + } else { + pending_load_icon_calls_.push_back(std::move(pending)); } + return; } // On failure, we still run the callback, with the zero IconValue.
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h index 9e51c6e5..f6f31b8 100644 --- a/chrome/browser/apps/app_service/arc_apps.h +++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -73,8 +73,6 @@ const std::string& name) override; void OnAppLastLaunchTimeUpdated(const std::string& app_id) override; - void ObservePrefs(); - const base::FilePath GetCachedIconFilePath(const std::string& app_id, int32_t size_hint_in_dip); void LoadIconFromVM(const std::string icon_key_s_key,
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc new file mode 100644 index 0000000..54d4260fd --- /dev/null +++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -0,0 +1,240 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/apps/app_service/crostini_apps.h" + +#include <utility> + +#include "chrome/browser/apps/app_service/app_icon_factory.h" +#include "chrome/browser/apps/app_service/app_service_proxy.h" +#include "chrome/browser/apps/app_service/launch_util.h" +#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h" +#include "chrome/browser/chromeos/crostini/crostini_util.h" +#include "chrome/grit/chrome_unscaled_resources.h" + +// TODO(crbug.com/826982): the equivalent of +// CrostiniAppModelBuilder::MaybeCreateRootFolder. Does some sort of "root +// folder" abstraction belong here (on the publisher side of the App Service) +// or should we hard-code that in one particular subscriber (the App List UI)? + +namespace apps { + +CrostiniApps::CrostiniApps() + : binding_(this), registry_(nullptr), next_u_key_(1) {} + +CrostiniApps::~CrostiniApps() { + if (registry_) { + registry_->RemoveObserver(this); + } +} + +void CrostiniApps::Initialize(const apps::mojom::AppServicePtr& app_service, + Profile* profile) { + if (!crostini::IsCrostiniUIAllowedForProfile(profile)) { + return; + } + registry_ = crostini::CrostiniRegistryServiceFactory::GetForProfile(profile); + if (!registry_) { + return; + } + + registry_->AddObserver(this); + + apps::mojom::PublisherPtr publisher; + binding_.Bind(mojo::MakeRequest(&publisher)); + app_service->RegisterPublisher(std::move(publisher), + apps::mojom::AppType::kCrostini); +} + +void CrostiniApps::Connect(apps::mojom::SubscriberPtr subscriber, + apps::mojom::ConnectOptionsPtr opts) { + std::vector<apps::mojom::AppPtr> apps; + for (const std::string& app_id : registry_->GetRegisteredAppIds()) { + base::Optional<crostini::CrostiniRegistryService::Registration> + registration = registry_->GetRegistration(app_id); + if (registration.has_value()) { + apps.push_back(Convert(app_id, *registration, true)); + } + } + subscriber->OnApps(std::move(apps)); + subscribers_.AddPtr(std::move(subscriber)); +} + +void CrostiniApps::LoadIcon(const std::string& app_id, + apps::mojom::IconKeyPtr icon_key, + apps::mojom::IconCompression icon_compression, + int32_t size_hint_in_dip, + LoadIconCallback callback) { + if (!icon_key.is_null()) { + if ((icon_key->icon_type == apps::mojom::IconType::kResource) && + (icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) { + int resource_id = static_cast<int>(icon_key->u_key); + LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id, + std::move(callback)); + return; + } + + if (icon_key->icon_type == apps::mojom::IconType::kCrostini) { + // TODO(crbug.com/826982): don't hard-code SCALE_FACTOR_100P. + auto scale_factor = ui::ScaleFactor::SCALE_FACTOR_100P; + + // Try loading the icon from an on-disk cache. If that fails, fall back + // to LoadIconFromVM. + LoadIconFromFileWithFallback( + icon_compression, size_hint_in_dip, + registry_->GetIconPath(icon_key->s_key, scale_factor), + std::move(callback), + base::BindOnce(&CrostiniApps::LoadIconFromVM, + weak_ptr_factory_.GetWeakPtr(), icon_key->s_key, + scale_factor)); + return; + } + } + + // On failure, we still run the callback, with the zero IconValue. + std::move(callback).Run(apps::mojom::IconValue::New()); +} + +void CrostiniApps::Launch(const std::string& app_id, + int32_t event_flags, + apps::mojom::LaunchSource launch_source, + int64_t display_id) { + apps_util::Launch(app_id, event_flags, launch_source, display_id); +} + +void CrostiniApps::SetPermission(const std::string& app_id, + apps::mojom::PermissionPtr permission) { + NOTIMPLEMENTED(); +} + +void CrostiniApps::Uninstall(const std::string& app_id) { + NOTIMPLEMENTED(); +} + +void CrostiniApps::OpenNativeSettings(const std::string& app_id) { + NOTIMPLEMENTED(); +} + +void CrostiniApps::OnRegistryUpdated( + crostini::CrostiniRegistryService* registry_service, + const std::vector<std::string>& updated_apps, + const std::vector<std::string>& removed_apps, + const std::vector<std::string>& inserted_apps) { + for (const std::string& app_id : updated_apps) { + PublishAppID(app_id, PublishAppIDType::kUpdate); + } + for (const std::string& app_id : removed_apps) { + PublishAppID(app_id, PublishAppIDType::kUninstall); + } + for (const std::string& app_id : inserted_apps) { + PublishAppID(app_id, PublishAppIDType::kInstall); + } +} + +void CrostiniApps::OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) { + apps::mojom::AppPtr app = apps::mojom::App::New(); + app->app_type = apps::mojom::AppType::kCrostini; + app->app_id = app_id; + app->icon_key = NewIconKey(app_id); + Publish(std::move(app)); +} + +void CrostiniApps::LoadIconFromVM(const std::string icon_key_s_key, + ui::ScaleFactor scale_factor, + LoadIconCallback callback) { + // Treat this as failure. We still run the callback, with the zero IconValue. + std::move(callback).Run(apps::mojom::IconValue::New()); + + // Ask the VM to load the icon (and write a cached copy to the file system). + // The "Maybe" is because multiple requests for the same icon will be merged, + // calling OnAppIconUpdated only once. In OnAppIconUpdated, we'll publish a + // new IconKey, and subscribers can re-schedule new LoadIcon calls, with new + // LoadIconCallback's, that will pick up that cached copy. + // + // TODO(crbug.com/826982): add a safeguard to prevent an infinite loop where + // OnAppIconUpdated somehow doesn't write the cached icon file where we + // expect, leading to another MaybeRequestIcon call, leading to another + // OnAppIconUpdated call, leading to another MaybeRequestIcon call, etc. + registry_->MaybeRequestIcon(icon_key_s_key, scale_factor); +} + +apps::mojom::AppPtr CrostiniApps::Convert( + const std::string& app_id, + const crostini::CrostiniRegistryService::Registration& registration, + bool new_icon_key) { + apps::mojom::AppPtr app = apps::mojom::App::New(); + + app->app_type = apps::mojom::AppType::kCrostini; + app->app_id = app_id; + app->readiness = apps::mojom::Readiness::kReady; + app->name = registration.Name(); + + if (new_icon_key) { + app->icon_key = NewIconKey(app_id); + } + + app->last_launch_time = registration.LastLaunchTime(); + app->install_time = registration.InstallTime(); + + app->installed_internally = apps::mojom::OptionalBool::kFalse; + + // TODO(crbug.com/826982): if Crostini isn't enabled, don't show the Terminal + // item until it becomes enabled. + auto show = !registration.NoDisplay() ? apps::mojom::OptionalBool::kTrue + : apps::mojom::OptionalBool::kFalse; + app->show_in_launcher = show; + app->show_in_search = show; + + return app; +} + +apps::mojom::IconKeyPtr CrostiniApps::NewIconKey(const std::string& app_id) { + auto icon_key = apps::mojom::IconKey::New(); + + // Treat the Crostini Terminal as a special case, loading an icon defined by + // a resource instead of asking the Crostini VM (or the cache of previous + // responses from the Crostini VM). Presumably this is for bootstrapping: the + // Crostini Terminal icon (the UI for enabling and installing Crostini apps) + // should be showable even before the user has installed their first Crostini + // app and before bringing up an Crostini VM for the first time. + if (app_id == crostini::kCrostiniTerminalId) { + icon_key->icon_type = apps::mojom::IconType::kResource; + icon_key->u_key = IDR_LOGO_CROSTINI_TERMINAL; + } else { + icon_key->icon_type = apps::mojom::IconType::kCrostini; + icon_key->s_key = app_id; + icon_key->u_key = next_u_key_++; + } + + return icon_key; +} + +void CrostiniApps::PublishAppID(const std::string& app_id, + PublishAppIDType type) { + if (type == PublishAppIDType::kUninstall) { + apps::mojom::AppPtr app = apps::mojom::App::New(); + app->app_type = apps::mojom::AppType::kCrostini; + app->app_id = app_id; + app->readiness = apps::mojom::Readiness::kUninstalledByUser; + Publish(std::move(app)); + return; + } + + base::Optional<crostini::CrostiniRegistryService::Registration> registration = + registry_->GetRegistration(app_id); + if (registration.has_value()) { + Publish(Convert(app_id, *registration, type == PublishAppIDType::kInstall)); + } +} + +void CrostiniApps::Publish(apps::mojom::AppPtr app) { + subscribers_.ForAllPtrs([&app](apps::mojom::Subscriber* subscriber) { + std::vector<apps::mojom::AppPtr> apps; + apps.push_back(app.Clone()); + subscriber->OnApps(std::move(apps)); + }); +} + +} // namespace apps
diff --git a/chrome/browser/apps/app_service/crostini_apps.h b/chrome/browser/apps/app_service/crostini_apps.h new file mode 100644 index 0000000..148c6316 --- /dev/null +++ b/chrome/browser/apps/app_service/crostini_apps.h
@@ -0,0 +1,98 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_APPS_APP_SERVICE_CROSTINI_APPS_H_ +#define CHROME_BROWSER_APPS_APP_SERVICE_CROSTINI_APPS_H_ + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/crostini/crostini_registry_service.h" +#include "chrome/services/app_service/public/mojom/app_service.mojom.h" +#include "components/keyed_service/core/keyed_service.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_ptr_set.h" + +class Profile; + +namespace apps { + +// An app publisher (in the App Service sense) of Crostini apps, +// +// See chrome/services/app_service/README.md. +class CrostiniApps : public KeyedService, + public apps::mojom::Publisher, + public crostini::CrostiniRegistryService::Observer { + public: + CrostiniApps(); + ~CrostiniApps() override; + + void Initialize(const apps::mojom::AppServicePtr& app_service, + Profile* profile); + + private: + enum class PublishAppIDType { + kInstall, + kUninstall, + kUpdate, + }; + + // apps::mojom::Publisher overrides. + void Connect(apps::mojom::SubscriberPtr subscriber, + apps::mojom::ConnectOptionsPtr opts) override; + void LoadIcon(const std::string& app_id, + apps::mojom::IconKeyPtr icon_key, + apps::mojom::IconCompression icon_compression, + int32_t size_hint_in_dip, + LoadIconCallback callback) override; + void Launch(const std::string& app_id, + int32_t event_flags, + apps::mojom::LaunchSource launch_source, + int64_t display_id) override; + void SetPermission(const std::string& app_id, + apps::mojom::PermissionPtr permission) override; + void Uninstall(const std::string& app_id) override; + void OpenNativeSettings(const std::string& app_id) override; + + // CrostiniRegistryService::Observer overrides. + void OnRegistryUpdated( + crostini::CrostiniRegistryService* registry_service, + const std::vector<std::string>& updated_apps, + const std::vector<std::string>& removed_apps, + const std::vector<std::string>& inserted_apps) override; + void OnAppIconUpdated(const std::string& app_id, + ui::ScaleFactor scale_factor) override; + + void LoadIconFromVM(const std::string icon_key_s_key, + ui::ScaleFactor scale_factor, + LoadIconCallback callback); + + apps::mojom::AppPtr Convert( + const std::string& app_id, + const crostini::CrostiniRegistryService::Registration& registration, + bool new_icon_key); + apps::mojom::IconKeyPtr NewIconKey(const std::string& app_id); + void PublishAppID(const std::string& app_id, PublishAppIDType type); + void Publish(apps::mojom::AppPtr app); + + mojo::Binding<apps::mojom::Publisher> binding_; + mojo::InterfacePtrSet<apps::mojom::Subscriber> subscribers_; + + crostini::CrostiniRegistryService* registry_; + + // |next_u_key_| is incremented every time Convert returns a valid AppPtr, so + // that when an app's icon has changed, this apps::mojom::Publisher sends a + // different IconKey even though the IconKey's s_key hasn't changed. + uint64_t next_u_key_; + + base::WeakPtrFactory<CrostiniApps> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(CrostiniApps); +}; + +} // namespace apps + +#endif // CHROME_BROWSER_APPS_APP_SERVICE_CROSTINI_APPS_H_
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc index c82e0d8..da3f2ce 100644 --- a/chrome/browser/apps/app_service/extension_apps.cc +++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -8,9 +8,9 @@ #include <utility> #include <vector> -#include "ash/public/cpp/shelf_types.h" #include "base/strings/string16.h" #include "chrome/browser/apps/app_service/app_icon_factory.h" +#include "chrome/browser/apps/app_service/launch_util.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_util.h" @@ -18,7 +18,6 @@ #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" #include "chrome/browser/ui/app_list/extension_app_utils.h" #include "chrome/browser/ui/app_list/search/search_util.h" -#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/common/extensions/extension_metrics.h" @@ -59,21 +58,6 @@ CONTENT_SETTINGS_TYPE_NOTIFICATIONS, }; -ash::ShelfLaunchSource ConvertLaunchSource( - apps::mojom::LaunchSource launch_source) { - switch (launch_source) { - case apps::mojom::LaunchSource::kUnknown: - return ash::LAUNCH_FROM_UNKNOWN; - case apps::mojom::LaunchSource::kFromAppListGrid: - case apps::mojom::LaunchSource::kFromAppListGridContextMenu: - return ash::LAUNCH_FROM_APP_LIST; - case apps::mojom::LaunchSource::kFromAppListQuery: - case apps::mojom::LaunchSource::kFromAppListQueryContextMenu: - case apps::mojom::LaunchSource::kFromAppListRecommendation: - return ash::LAUNCH_FROM_APP_LIST_SEARCH; - } -} - } // namespace namespace apps { @@ -191,9 +175,7 @@ break; } - ChromeLauncherController::instance()->LaunchApp( - ash::ShelfID(app_id), ConvertLaunchSource(launch_source), event_flags, - display_id); + apps_util::Launch(app_id, event_flags, launch_source, display_id); } void ExtensionApps::SetPermission(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/launch_util.cc b/chrome/browser/apps/app_service/launch_util.cc new file mode 100644 index 0000000..6ccd1b0e --- /dev/null +++ b/chrome/browser/apps/app_service/launch_util.cc
@@ -0,0 +1,40 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/apps/app_service/launch_util.h" + +#include "ash/public/cpp/shelf_types.h" +#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" + +namespace { + +ash::ShelfLaunchSource ConvertLaunchSource( + apps::mojom::LaunchSource launch_source) { + switch (launch_source) { + case apps::mojom::LaunchSource::kUnknown: + return ash::LAUNCH_FROM_UNKNOWN; + case apps::mojom::LaunchSource::kFromAppListGrid: + case apps::mojom::LaunchSource::kFromAppListGridContextMenu: + return ash::LAUNCH_FROM_APP_LIST; + case apps::mojom::LaunchSource::kFromAppListQuery: + case apps::mojom::LaunchSource::kFromAppListQueryContextMenu: + case apps::mojom::LaunchSource::kFromAppListRecommendation: + return ash::LAUNCH_FROM_APP_LIST_SEARCH; + } +} + +} // namespace + +namespace apps_util { + +void Launch(const std::string& app_id, + int32_t event_flags, + apps::mojom::LaunchSource launch_source, + int64_t display_id) { + ChromeLauncherController::instance()->LaunchApp( + ash::ShelfID(app_id), ConvertLaunchSource(launch_source), event_flags, + display_id); +} + +} // namespace apps_util
diff --git a/chrome/browser/apps/app_service/launch_util.h b/chrome/browser/apps/app_service/launch_util.h new file mode 100644 index 0000000..30afbdb --- /dev/null +++ b/chrome/browser/apps/app_service/launch_util.h
@@ -0,0 +1,23 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_UTIL_H_ +#define CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_UTIL_H_ + +// Utility functions for launching an app. + +#include <string> + +#include "chrome/services/app_service/public/mojom/types.mojom.h" + +namespace apps_util { + +void Launch(const std::string& app_id, + int32_t event_flags, + apps::mojom::LaunchSource launch_source, + int64_t display_id); + +} // namespace apps_util + +#endif // CHROME_BROWSER_APPS_APP_SERVICE_LAUNCH_UTIL_H_
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 70d2354..ec6d3ed 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -233,9 +233,9 @@ <include name="IDR_APP_MANAGEMENT_API_LISTENER_HTML" file="resources\app_management\api_listener.html" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_API_LISTENER_JS" file="resources\app_management\api_listener.js" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_APP_HTML" file="resources\app_management\app.html" type="BINDATA" /> - <include name="IDR_APP_MANAGEMENT_APP_JS" file="resources\app_management\app.js" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_APP_ITEM_HTML" file="resources\app_management\app_item.html" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_APP_ITEM_JS" file="resources\app_management\app_item.js" type="BINDATA" /> + <include name="IDR_APP_MANAGEMENT_APP_JS" file="resources\app_management\app.js" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_BROWSER_PROXY_HTML" file="resources\app_management\browser_proxy.html" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_BROWSER_PROXY_JS" file="resources\app_management\browser_proxy.js" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_CHROME_APP_PERMISSION_VIEW_HTML" file="resources\app_management\chrome_app_permission_view.html" type="BINDATA"/> @@ -246,14 +246,16 @@ <include name="IDR_APP_MANAGEMENT_INDEX_HTML" file="resources\app_management\index.html" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_MAIN_VIEW_HTML" file="resources\app_management\main_view.html" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_MAIN_VIEW_JS" file="resources\app_management\main_view.js" type="BINDATA" /> - <include name="IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_HTML" file="resources\app_management\notifications_view.html" type="BINDATA"/> - <include name="IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_JS" file="resources\app_management\notifications_view.js" type="BINDATA"/> - <include name="IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_HTML" file="resources\app_management\permission_view_header.html" type="BINDATA"/> - <include name="IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_JS" file="resources\app_management\permission_view_header.js" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_METADATA_VIEW_HTML" file="resources\app_management\metadata_view.html" type="BINDATA" /> <include name="IDR_APP_MANAGEMENT_METADATA_VIEW_JS" file="resources\app_management\metadata_view.js" type="BINDATA" /> + <include name="IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_HTML" file="resources\app_management\notifications_view.html" type="BINDATA"/> + <include name="IDR_APP_MANAGEMENT_NOTIFICATIONS_VIEW_JS" file="resources\app_management\notifications_view.js" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_PERMISSION_ITEM_HTML" file="resources\app_management\permission_item.html" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_PERMISSION_ITEM_JS" file="resources\app_management\permission_item.js" type="BINDATA"/> + <include name="IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_HTML" file="resources\app_management\permission_toggle.html" type="BINDATA"/> + <include name="IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_JS" file="resources\app_management\permission_toggle.js" type="BINDATA"/> + <include name="IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_HTML" file="resources\app_management\permission_view_header.html" type="BINDATA"/> + <include name="IDR_APP_MANAGEMENT_PERMISSION_VIEW_HEADER_JS" file="resources\app_management\permission_view_header.js" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_PWA_PERMISSION_VIEW_HTML" file="resources\app_management\pwa_permission_view.html" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_PWA_PERMISSION_VIEW_JS" file="resources\app_management\pwa_permission_view.js" type="BINDATA"/> <include name="IDR_APP_MANAGEMENT_REDUCERS_HTML" file="resources\app_management\reducers.html" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index d1b7c89..5ef0e74 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -128,6 +128,7 @@ #include "chrome/browser/ssl/insecure_sensitive_input_driver_factory.h" #include "chrome/browser/ssl/ssl_blocking_page.h" #include "chrome/browser/ssl/ssl_cert_reporter.h" +#include "chrome/browser/ssl/ssl_client_auth_metrics.h" #include "chrome/browser/ssl/ssl_client_certificate_selector.h" #include "chrome/browser/ssl/ssl_error_handler.h" #include "chrome/browser/ssl/ssl_error_navigation_throttle.h" @@ -2837,12 +2838,14 @@ std::move(auto_selected_identity), base::Bind(&content::ClientCertificateDelegate::ContinueWithCertificate, base::Passed(&delegate), std::move(cert))); + LogClientAuthResult(ClientCertSelectionResult::kAutoSelect); return; } if (!may_show_cert_selection) { LOG(WARNING) << "No client cert matched by policy and user selection is " "not allowed."; + LogClientAuthResult(ClientCertSelectionResult::kNoSelectionAllowed); // Continue without client certificate. We do this to mimic the case of no // client certificate being present in the profile's certificate store. delegate->ContinueWithCertificate(nullptr, nullptr);
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index 2b207a9..13af0b3 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1695,6 +1695,9 @@ if (start_session_type_ == PRIMARY_USER_SESSION) { #if BUILDFLAG(ENABLE_CROS_ASSISTANT) // Initialize Assistant early to be used in post login Oobe steps. + // Note: AssistantClient::MaybeInit is also called in + // SessionControllerClient::OnSessionStateChanged, which happends after the + // post login Oobe steps. Therefore Assistant is initialized here. if (chromeos::switches::IsAssistantEnabled()) AssistantClient::Get()->MaybeInit(profile); #endif
diff --git a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc index 741b2cb6..02018d9 100644 --- a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc +++ b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -31,6 +31,7 @@ #include "device/usb/public/cpp/fake_usb_device_info.h" #include "device/usb/public/cpp/fake_usb_device_manager.h" #include "device/usb/public/mojom/device.mojom.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" #include "device/usb/public/mojom/device_manager.mojom.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc b/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc index 0c27b711..1c1673b 100644 --- a/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc +++ b/chrome/browser/devtools/device/usb/usb_device_manager_helper.cc
@@ -14,6 +14,7 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/service_manager_connection.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" #include "mojo/public/cpp/bindings/callback_helpers.h" #include "services/device/public/mojom/constants.mojom.h" #include "services/service_manager/public/cpp/connector.h"
diff --git a/chrome/browser/devtools/devtools_eye_dropper.cc b/chrome/browser/devtools/devtools_eye_dropper.cc index 55822d8..b1ce3098 100644 --- a/chrome/browser/devtools/devtools_eye_dropper.cc +++ b/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -20,6 +20,7 @@ #include "content/public/common/screen_info.h" #include "media/base/limits.h" #include "media/base/video_frame.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/public/platform/web_mouse_event.h" #include "third_party/skia/include/core/SkCanvas.h"
diff --git a/chrome/browser/download/download_dir_policy_handler.cc b/chrome/browser/download/download_dir_policy_handler.cc index ce9d5e1..699358c 100644 --- a/chrome/browser/download/download_dir_policy_handler.cc +++ b/chrome/browser/download/download_dir_policy_handler.cc
@@ -70,7 +70,7 @@ DownloadPrefs::GetDefaultDownloadDirectory().value()); } prefs->SetValue(prefs::kDownloadDefaultDirectory, - std::make_unique<base::Value>(expanded_value)); + base::Value(expanded_value)); // If the policy is mandatory, prompt for download should be disabled. // Otherwise, it would enable a user to bypass the mandatory policy.
diff --git a/chrome/browser/media/cast_mirroring_service_host.cc b/chrome/browser/media/cast_mirroring_service_host.cc index 286256a..8571183 100644 --- a/chrome/browser/media/cast_mirroring_service_host.cc +++ b/chrome/browser/media/cast_mirroring_service_host.cc
@@ -16,7 +16,10 @@ #include "chrome/browser/media/cast_remoting_connector.h" #include "chrome/browser/net/system_network_context_manager.h" #include "components/mirroring/browser/single_client_video_capture_host.h" +#include "components/mirroring/mojom/cast_message_channel.mojom.h" #include "components/mirroring/mojom/constants.mojom.h" +#include "components/mirroring/mojom/session_observer.mojom.h" +#include "components/mirroring/mojom/session_parameters.mojom.h" #include "content/public/browser/audio_loopback_stream_creator.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc index 1110a21..791b3ac 100644 --- a/chrome/browser/media/cast_mirroring_service_host_browsertest.cc +++ b/chrome/browser/media/cast_mirroring_service_host_browsertest.cc
@@ -11,9 +11,17 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/in_process_browser_test.h" +#include "components/mirroring/mojom/cast_message_channel.mojom.h" +#include "components/mirroring/mojom/session_observer.mojom.h" +#include "components/mirroring/mojom/session_parameters.mojom.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 "media/capture/mojom/video_capture.mojom.h" +#include "media/capture/mojom/video_capture_types.mojom.h" +#include "media/capture/video_capture_types.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" +#include "media/mojo/interfaces/audio_input_stream.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/base/ui_base_features.h"
diff --git a/chrome/browser/media/cast_remoting_connector.h b/chrome/browser/media/cast_remoting_connector.h index 33597f2..4c76098 100644 --- a/chrome/browser/media/cast_remoting_connector.h +++ b/chrome/browser/media/cast_remoting_connector.h
@@ -14,6 +14,7 @@ #include "components/sessions/core/session_id.h" #include "media/mojo/interfaces/mirror_service_remoting.mojom.h" #include "media/mojo/interfaces/remoting.mojom.h" +#include "media/mojo/interfaces/remoting_common.mojom.h" #include "mojo/public/cpp/bindings/binding.h" namespace content {
diff --git a/chrome/browser/payments/android/journey_logger_android.cc b/chrome/browser/payments/android/journey_logger_android.cc index 8280ce7..bf7b757 100644 --- a/chrome/browser/payments/android/journey_logger_android.cc +++ b/chrome/browser/payments/android/journey_logger_android.cc
@@ -78,6 +78,13 @@ journey_logger_.SetCanMakePaymentValue(jvalue); } +void JourneyLoggerAndroid::SetHasEnrolledInstrumentValue( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + jboolean jvalue) { + journey_logger_.SetHasEnrolledInstrumentValue(jvalue); +} + void JourneyLoggerAndroid::SetEventOccurred( JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/payments/android/journey_logger_android.h b/chrome/browser/payments/android/journey_logger_android.h index ed9a062..fbc2598 100644 --- a/chrome/browser/payments/android/journey_logger_android.h +++ b/chrome/browser/payments/android/journey_logger_android.h
@@ -44,6 +44,10 @@ JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, jboolean jvalue); + void SetHasEnrolledInstrumentValue( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + jboolean jvalue); void SetEventOccurred(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, jint jevent);
diff --git a/chrome/browser/profiling_host/memlog_browsertest.cc b/chrome/browser/profiling_host/memlog_browsertest.cc index f3ffae1..09f81a1 100644 --- a/chrome/browser/profiling_host/memlog_browsertest.cc +++ b/chrome/browser/profiling_host/memlog_browsertest.cc
@@ -34,7 +34,6 @@ struct TestParam { Mode mode; mojom::StackMode stack_mode; - bool stream_samples; bool start_profiling_with_command_line_flag; bool should_sample; bool sample_everything; @@ -93,7 +92,6 @@ IN_PROC_BROWSER_TEST_P(MemlogBrowserTest, MAYBE_EndToEnd) { LOG(INFO) << "Memlog mode: " << static_cast<int>(GetParam().mode); LOG(INFO) << "Memlog stack mode: " << static_cast<int>(GetParam().stack_mode); - LOG(INFO) << "Stream samples: " << GetParam().stream_samples; LOG(INFO) << "Started via command line flag: " << GetParam().start_profiling_with_command_line_flag; LOG(INFO) << "Should sample: " << GetParam().should_sample; @@ -102,7 +100,6 @@ TestDriver::Options options; options.mode = GetParam().mode; options.stack_mode = GetParam().stack_mode; - options.stream_samples = GetParam().stream_samples; options.profiling_already_started = GetParam().start_profiling_with_command_line_flag; options.should_sample = GetParam().should_sample; @@ -126,60 +123,54 @@ stack_modes.push_back(mojom::StackMode::NATIVE_WITHOUT_THREAD_NAMES); stack_modes.push_back(mojom::StackMode::PSEUDO); - for (bool stream_samples : (bool[]){true, false}) { - for (const auto& mode : dynamic_start_modes) { - for (const auto& stack_mode : stack_modes) { - params.push_back({mode, stack_mode, stream_samples, - false /* start_profiling_with_command_line_flag */, - false /* should_sample */, - false /* sample_everything*/}); - } + for (const auto& mode : dynamic_start_modes) { + for (const auto& stack_mode : stack_modes) { + params.push_back( + {mode, stack_mode, false /* start_profiling_with_command_line_flag */, + false /* should_sample */, false /* sample_everything*/}); } + } // For unknown reasons, renderer profiling has become flaky on ChromeOS. This is // likely happening because the renderers are never being given the signal to // start profiling. It's unclear why this happens. https://crbug.com/843843. // https://crbug.com/843467. #if !defined(OS_CHROMEOS) - // Non-browser processes must be profiled with a command line flag, since - // otherwise, profiling will start after the relevant processes have been - // created, thus that process will be not be profiled. - std::vector<Mode> command_line_start_modes; - command_line_start_modes.push_back(Mode::kAll); - command_line_start_modes.push_back(Mode::kAllRenderers); - for (const auto& mode : command_line_start_modes) { - for (const auto& stack_mode : stack_modes) { - params.push_back({mode, stack_mode, stream_samples, - true /* start_profiling_with_command_line_flag */, - false /* should_sample */, - false /* sample_everything*/}); - } + // Non-browser processes must be profiled with a command line flag, since + // otherwise, profiling will start after the relevant processes have been + // created, thus that process will be not be profiled. + std::vector<Mode> command_line_start_modes; + command_line_start_modes.push_back(Mode::kAll); + command_line_start_modes.push_back(Mode::kAllRenderers); + for (const auto& mode : command_line_start_modes) { + for (const auto& stack_mode : stack_modes) { + params.push_back( + {mode, stack_mode, true /* start_profiling_with_command_line_flag */, + false /* should_sample */, false /* sample_everything*/}); } + } #endif // defined(OS_CHROMEOS) - // Test sampling all allocations. - params.push_back( - {Mode::kBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES, - stream_samples, false /* start_profiling_with_command_line_flag */, - true /* should_sample */, true /* sample_everything*/}); + // Test sampling all allocations. + params.push_back({Mode::kBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES, + false /* start_profiling_with_command_line_flag */, + true /* should_sample */, true /* sample_everything*/}); - // Test sampling some allocations. - params.push_back({Mode::kBrowser, mojom::StackMode::PSEUDO, stream_samples, - false /* start_profiling_with_command_line_flag */, - true /* should_sample */, false /* sample_everything*/}); + // Test sampling some allocations. + params.push_back({Mode::kBrowser, mojom::StackMode::PSEUDO, + false /* start_profiling_with_command_line_flag */, + true /* should_sample */, false /* sample_everything*/}); - // Test thread names for native profiling. - params.push_back({Mode::kBrowser, - mojom::StackMode::NATIVE_WITH_THREAD_NAMES, - stream_samples, false}); + // Test thread names for native profiling. + params.push_back( + {Mode::kBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES, false}); - // Profile all utility processes and the browser process. The main goal is - // to check that there is no deadlock in the profiling process. - params.push_back( - {Mode::kUtilityAndBrowser, mojom::StackMode::NATIVE_WITH_THREAD_NAMES, - stream_samples, false /* start_profiling_with_command_line_flag */, - true /* should_sample */, false /* sample_everything*/}); - } + // Profile all utility processes and the browser process. The main goal is to + // check that there is no deadlock in the profiling process. + params.push_back({Mode::kUtilityAndBrowser, + mojom::StackMode::NATIVE_WITH_THREAD_NAMES, + false /* start_profiling_with_command_line_flag */, + true /* should_sample */, false /* sample_everything*/}); return params; }
diff --git a/chrome/browser/resources/app_management/app.js b/chrome/browser/resources/app_management/app.js index 2c5086a..a200919 100644 --- a/chrome/browser/resources/app_management/app.js +++ b/chrome/browser/resources/app_management/app.js
@@ -47,7 +47,7 @@ }); this.watch('notificationsViewSelected_', (state) => { - return state.currentPage.pageType === PageType.NOTIFICATIONS; + return app_management.util.isNotificationsViewSelected(state); }); this.updateFromStore();
diff --git a/chrome/browser/resources/app_management/browser_proxy.js b/chrome/browser/resources/app_management/browser_proxy.js index ca9833f..b9d030cc 100644 --- a/chrome/browser/resources/app_management/browser_proxy.js +++ b/chrome/browser/resources/app_management/browser_proxy.js
@@ -20,16 +20,16 @@ const /** @type {!Array<App>}*/ appList = [ app_management.FakePageHandler.createApp( - 'ahfgeienlihckogmohjhadlkjgocpleb'), + 'ahfgeienlihckogmohjhadlkjgocpleb', {title: 'Web Store'}), app_management.FakePageHandler.createApp( 'aohghmighlieiainnegkcijnfilokake', - {type: apps.mojom.AppType.kExtension}), + {title: 'Docs', type: apps.mojom.AppType.kExtension}), app_management.FakePageHandler.createApp( - 'blpcfgokakmgnkcojhhkbfbldkacnbeo'), + 'blpcfgokakmgnkcojhhkbfbldkacnbeo', {title: 'Youtube'}), app_management.FakePageHandler.createApp( - 'pjkljhegncpnkpknbcohdijeoejaedia'), + 'pjkljhegncpnkpknbcohdijeoejaedia', {title: 'Gmail'}), app_management.FakePageHandler.createApp( - 'aapocclcgogkmnckokdopfmhonfmgoek'), + 'aapocclcgogkmnckokdopfmhonfmgoek', {title: 'Slide'}), ]; this.handler.setApps(appList);
diff --git a/chrome/browser/resources/app_management/fake_page_handler.js b/chrome/browser/resources/app_management/fake_page_handler.js index 2d62f96..b7b90aa4 100644 --- a/chrome/browser/resources/app_management/fake_page_handler.js +++ b/chrome/browser/resources/app_management/fake_page_handler.js
@@ -13,8 +13,10 @@ * @return {!Permission} */ static createPermission(permissionId, optConfig) { + // Changing to kAllow to test notifications sublabel collapsibility, as it + // assumes all apps have notification permission. const permission = app_management.util.createPermission( - permissionId, PermissionValueType.kTriState, TriState.kBlock); + permissionId, PermissionValueType.kTriState, TriState.kAllow); if (optConfig) { Object.assign(permission, optConfig);
diff --git a/chrome/browser/resources/app_management/main_view.html b/chrome/browser/resources/app_management/main_view.html index 96e7bf8..50347e4 100644 --- a/chrome/browser/resources/app_management/main_view.html +++ b/chrome/browser/resources/app_management/main_view.html
@@ -80,7 +80,7 @@ <template is="dom-repeat" items="[[collapsedApps_]]"> <app-management-app-item app="[[item]]"> <paper-icon-button-light slot="right-content" - class="subpage-arrow" actionable> + class="subpage-arrow app-management-item-arrow" actionable> <button></button> </paper-icon-button-light> </app-management-app-item>
diff --git a/chrome/browser/resources/app_management/main_view.js b/chrome/browser/resources/app_management/main_view.js index 56b06c54..c1b75baf 100644 --- a/chrome/browser/resources/app_management/main_view.js +++ b/chrome/browser/resources/app_management/main_view.js
@@ -58,9 +58,7 @@ }, attached: function() { - this.watch('apps_', function(state) { - return Object.values(state.apps); - }); + this.watch('apps_', state => Object.values(state.apps)); this.updateFromStore(); }, @@ -94,7 +92,10 @@ this.displayedApps_ = this.apps_.slice(0, NUMBER_OF_APPS_DISPLAYED_DEFAULT); this.collapsedApps_ = this.apps_.slice(NUMBER_OF_APPS_DISPLAYED_DEFAULT, this.apps_.length); - this.notificationApps_ = this.apps_.slice(); + + const [notificationsAllowed, notificationsBlocked] = + app_management.util.splitByNotificationPermission(); + this.notificationApps_ = notificationsAllowed; }, /** @@ -157,7 +158,7 @@ const textContainer = this.$['notifications-sublabel']; textContainer.textContent = ''; for (const p of pieces) { - if (p.value.length == 0) { + if (!p.value || p.value.length == 0) { return; }
diff --git a/chrome/browser/resources/app_management/metadata_view.js b/chrome/browser/resources/app_management/metadata_view.js index 8914ee5..93a9a4d 100644 --- a/chrome/browser/resources/app_management/metadata_view.js +++ b/chrome/browser/resources/app_management/metadata_view.js
@@ -41,6 +41,7 @@ return app.isPinned === OptionalBool.kTrue; }, + /** @private */ togglePinned_: function() { let newPinnedValue; @@ -84,5 +85,4 @@ return loadTimeData.getStringF('size', assert(app.size)); }, - });
diff --git a/chrome/browser/resources/app_management/notifications_view.html b/chrome/browser/resources/app_management/notifications_view.html index 8b748633..ae66705a 100644 --- a/chrome/browser/resources/app_management/notifications_view.html +++ b/chrome/browser/resources/app_management/notifications_view.html
@@ -3,6 +3,7 @@ <link rel="import" href="app_item.html"> <link rel="import" href="shared_style.html"> <link rel="import" href="store_client.html"> +<link rel="import" href="permission_toggle.html"> <link rel="import" href="chrome://resources/html/icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> @@ -10,7 +11,7 @@ <dom-module id="app-management-notifications-view"> <template> <style include="app-management-shared-css"> - cr-toggle { + app-management-permission-toggle { margin-inline-end: 24px; } @@ -26,15 +27,17 @@ } #notification-title { - padding-start: 12px; + padding-inline-start: 12px; } </style> <!-- TODO(ceciliani) Make this view a separate element to avoid duplicate code with main view --> + <!-- TODO(crbug.com/906508): Implement display when there is no apps at + all --> <div id="notification-view-header"> <paper-icon-button-light class="icon-arrow-back"> <button id="closeButton" on-click="onClickBackButton_" - aria-label="$i18n{back}"> + aria-label="$i18n{back}"> <paper-ripple class="circle"></paper-ripple> </button> </paper-icon-button-light> @@ -44,20 +47,27 @@ <div class="card-container"> <template is="dom-repeat" items="[[displayedApps_]]"> <app-management-app-item app="[[item]]"> - <cr-toggle slot="right-content"></cr-toggle> + <app-management-permission-toggle slot="right-content" + app="[[item]]" + permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS"> + </app-management-permission-toggle> </app-management-app-item> </template> <iron-collapse opened="[[listExpanded_]]"> <template is="dom-repeat" items="[[collapsedApps_]]"> <app-management-app-item app="[[item]]"> - <cr-toggle slot="right-content"></cr-toggle> + <app-management-permission-toggle slot="right-content" + app="[[item]]" + permission-type="CONTENT_SETTINGS_TYPE_NOTIFICATIONS"> + </app-management-permission-toggle> </app-management-app-item> </template> </iron-collapse> - <div class="expander-list-row" on-click="toggleListExpanded_"> - <span>[[moreAppsString_(apps_.length,listExpanded_)]]</span> + <div id="expander-row" class="expander-list-row" + on-click="toggleListExpanded_"> + <span>[[moreAppsString_(listExpanded_, collapsedApps_)]]</span> <paper-icon-button-light class="expand-button"> <button> <iron-icon icon="[[getCollapsedIcon_(listExpanded_)]]">
diff --git a/chrome/browser/resources/app_management/notifications_view.js b/chrome/browser/resources/app_management/notifications_view.js index 7653db2..f1ebef8 100644 --- a/chrome/browser/resources/app_management/notifications_view.js +++ b/chrome/browser/resources/app_management/notifications_view.js
@@ -11,35 +11,11 @@ properties: { /** - * List of all apps. - * @private {Array<App>} - */ - apps_: { - type: Array, - value: () => [], - }, - - /** * List of apps with notification permission * displayed before expanding the app list. * @private {Array<App>} */ - displayedApps_: { - type: Array, - value: function() { - const /** @type {!Array<App>}*/ appList = [ - app_management.FakePageHandler.createApp( - 'blpcfgokakmgnkcojhhkbfbldkacnbeo'), - app_management.FakePageHandler.createApp( - 'pjkljhegncpnkpknbcohdijeoejaedia'), - app_management.FakePageHandler.createApp( - 'aapocclcgogkmnckokdopfmhonfmgoek'), - app_management.FakePageHandler.createApp( - 'ahfgeienlihckogmohjhadlkjgocpleb'), - ]; - return appList; - }, - }, + displayedApps_: Array, /** * List of apps without notification permission @@ -48,14 +24,7 @@ */ collapsedApps_: { type: Array, - value: function() { - const /** @type {!Array<App>}*/ appList = [ - app_management.FakePageHandler.createApp( - 'aohghmighlieiainnegkcijnfilokake', - {type: apps.mojom.AppType.kArc}), - ]; - return appList; - }, + value: () => [], }, /** @@ -63,30 +32,53 @@ */ listExpanded_: { type: Boolean, - value: true, + value: false, + }, + + /** + * A boolean to identify whether current page is notifications view so that + * apps' list only get updated when page refreshed. + * @private {boolean} + */ + notificationsViewSelected_: { + type: Boolean, + observer: 'onViewChanged_', }, }, attached: function() { - this.watch('apps_', (state) => { - return Object.values(state.apps); + this.watch('notificationsViewSelected_', (state) => { + return app_management.util.isNotificationsViewSelected(state); }); this.updateFromStore(); }, /** - * @param {number} numApps + * If all the apps have / don't have notification permission, display the + * whole list, else display those with notification permission before + * expanding. + * @private + */ + onViewChanged_: function() { + [this.displayedApps_, this.collapsedApps_] = + app_management.util.splitByNotificationPermission(); + if (this.displayedApps_.length === 0) { + this.displayedApps_ = this.collapsedApps_; + this.collapsedApps_ = []; + } + this.$['expander-row'].hidden = this.collapsedApps_.length === 0; + }, + + /** * @param {boolean} listExpanded + * @param {Array<App>} collapsedApps * @return {string} * @private */ - moreAppsString_: function(numApps, listExpanded) { - // TODO(ceciliani) Make view display apps with notification permission - // first. + moreAppsString_: function(listExpanded, collapsedApps) { return listExpanded ? loadTimeData.getString('lessApps') : - loadTimeData.getStringF( - 'moreApps', numApps - NUMBER_OF_APPS_DISPLAYED_DEFAULT); + loadTimeData.getStringF('moreApps', collapsedApps.length); }, /** @@ -98,6 +90,7 @@ /** @private */ onClickBackButton_: function() { + this.listExpanded_ = false; if (!window.history.state) { this.dispatch(app_management.actions.changePage(PageType.MAIN)); } else { @@ -113,4 +106,17 @@ getCollapsedIcon_: function(listExpanded) { return listExpanded ? 'cr:expand-less' : 'cr:expand-more'; }, + + /** + * TODO(rekanorman) Make this function work for other apps. + * Returns a boolean representation of the permission value, which used to + * determine the position of the permission toggle. + * @param {App} app + * @return {boolean} + * @private + */ + getNotificationValueBool_: function(app) { + return app_management.util.getPermissionValueBool( + app, 'CONTENT_SETTINGS_TYPE_NOTIFICATIONS'); + }, });
diff --git a/chrome/browser/resources/app_management/permission_item.html b/chrome/browser/resources/app_management/permission_item.html index ab33c29..3b41326 100644 --- a/chrome/browser/resources/app_management/permission_item.html +++ b/chrome/browser/resources/app_management/permission_item.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="shared_style.html"> +<link rel="import" href="permission_toggle.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> @@ -26,10 +27,9 @@ </template> <div id="permission-label">[[permissionLabel]]</div> </div> - <cr-toggle - checked="[[permissionValueBool_(permissionValueType_, permissionValue_)]]" - on-change="togglePermission_"> - </cr-toggle> + <app-management-permission-toggle + app="[[app_]]" permission-type="[[permissionType]]"> + </app-management-permission-toggle> </template> <script src="permission_item.js"></script> </dom-module>
diff --git a/chrome/browser/resources/app_management/permission_item.js b/chrome/browser/resources/app_management/permission_item.js index 1a885f5..ae87966 100644 --- a/chrome/browser/resources/app_management/permission_item.js +++ b/chrome/browser/resources/app_management/permission_item.js
@@ -27,24 +27,6 @@ */ app_: Object, - /** - * @private {PermissionValueType} - */ - permissionValueType_: { - type: Number, - computed: 'getPermissionValueType_(app_)', - }, - - /** - * The semantics of permissionValue_ depend on permissionValueType_, - * see chrome/services/app_service/public/mojom/types.mojom - * @private {?number} - */ - permissionValue_: { - type: Number, - computed: 'getPermissionValue_(app_, permissionType)', - }, - /** @type {string} */ icon: String, }, @@ -53,128 +35,4 @@ this.watch('app_', state => app_management.util.getSelectedApp(state)); this.updateFromStore(); }, - - /** - * @param {App} app - * @return {number} - * @private - */ - getPermissionValueType_: function(app) { - // TODO(rekanorman): Change to a suitable conditional statement once the - // PermissionValueType corresponding to each AppType is known. - return PermissionValueType.kTriState; - }, - - /** - * @param {App} app - * @param {string} permissionType - * @return {?number} - * @private - */ - getPermissionValue_: function(app, permissionType) { - if (!app) { - return null; - } - - // TODO(rekanorman): Remove once permissions are implemented for all - // app types. - if (Object.keys(app.permissions).length === 0) { - return null; - } - - // TODO(rekanorman): Make generic once permissions are implemented for - // other app types. - return app.permissions[PwaPermissionType[permissionType]].value; - }, - - /** - * Returns a boolean representation of the permission value, which used to - * determine the position of the permission toggle. - * @param {PermissionValueType} permissionValueType - * @param {number} permissionValue - * @return {boolean} - * @private - */ - permissionValueBool_: function(permissionValueType, permissionValue) { - return (permissionValueType === PermissionValueType.kBool && - permissionValue === Bool.kTrue) || - (permissionValueType === PermissionValueType.kTriState && - permissionValue === TriState.kAllow); - }, - - /** - * @private - */ - togglePermission_: function() { - /** @type {!Permission} */ - let newPermission; - - switch (this.permissionValueType_) { - case PermissionValueType.kBool: - newPermission = this.getNewPermissionBoolean_(); - break; - case PermissionValueType.kTriState: - newPermission = this.getNewPermissionTriState_(); - break; - default: - assertNotReached(); - } - - app_management.BrowserProxy.getInstance().handler.setPermission( - this.app_.id, newPermission); - }, - - /** - * @private - * @return {!Permission} - */ - getNewPermissionBoolean_: function() { - /** @type {number} */ - let newPermissionValue; - - switch (this.permissionValue_) { - case Bool.kFalse: - newPermissionValue = Bool.kTrue; - break; - case Bool.kTrue: - newPermissionValue = Bool.kFalse; - break; - default: - assertNotReached(); - } - - return app_management.util.createPermission( - PwaPermissionType[this.permissionType], PermissionValueType.kBool, - newPermissionValue); - }, - - /** - * @private - * @return {!Permission} - */ - getNewPermissionTriState_: function() { - let newPermissionValue; - - switch (this.permissionValue_) { - case TriState.kBlock: - newPermissionValue = TriState.kAllow; - break; - case TriState.kAsk: - newPermissionValue = TriState.kAllow; - break; - case TriState.kAllow: - // TODO(rekanorman): Eventually TriState.kAsk, but currently changing a - // permission to kAsk then opening the site settings page for the app - // produces the error: - // "Only extensions or enterprise policy can change the setting to ASK." - newPermissionValue = TriState.kBlock; - break; - default: - assertNotReached(); - } - - return app_management.util.createPermission( - PwaPermissionType[this.permissionType], PermissionValueType.kTriState, - newPermissionValue); - }, });
diff --git a/chrome/browser/resources/app_management/permission_toggle.html b/chrome/browser/resources/app_management/permission_toggle.html new file mode 100644 index 0000000..c7a3017 --- /dev/null +++ b/chrome/browser/resources/app_management/permission_toggle.html
@@ -0,0 +1,12 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> + +<dom-module id="app-management-permission-toggle"> + <template> + <cr-toggle checked="[[getPermissionValueBool_(app, permissionType)]]" + on-change="togglePermission_"> + </cr-toggle> + </template> + <script src="permission_toggle.js"></script> +</dom-module>
diff --git a/chrome/browser/resources/app_management/permission_toggle.js b/chrome/browser/resources/app_management/permission_toggle.js new file mode 100644 index 0000000..826b3a8 --- /dev/null +++ b/chrome/browser/resources/app_management/permission_toggle.js
@@ -0,0 +1,113 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +Polymer({ + is: 'app-management-permission-toggle', + + properties: { + /** + * @type {App} + */ + app: Object, + + /** + * TODO(rekanorman) Change the annotation when app type can be correctly + * sent. + * A string version of the permission type, corresponding to a value of + * the PwaPermissionType enum. + * @type {string} + */ + permissionType: String, + }, + + /** + * @param {String} appId + * @param {string} permissionType + * @return {boolean} + */ + getPermissionValueBool_: function(app, permissionType) { + return app_management.util.getPermissionValueBool(app, permissionType); + }, + + /** + * @private + */ + togglePermission_: function() { + /** @type {!Permission} */ + let newPermission; + + switch (app_management.util.getPermissionValueType(this.app)) { + case PermissionValueType.kBool: + newPermission = + this.getNewPermissionBoolean_(this.app, this.permissionType); + break; + case PermissionValueType.kTriState: + newPermission = + this.getNewPermissionTriState_(this.app, this.permissionType); + break; + default: + assertNotReached(); + } + + app_management.BrowserProxy.getInstance().handler.setPermission( + this.app.id, newPermission); + }, + + /** + * @private + * @param {App} app + * @param {string} permissionType + * @return {!Permission} + */ + getNewPermissionBoolean_: function(app, permissionType) { + /** @type {number} */ + let newPermissionValue; + + switch (app_management.util.getPermissionValue(app, permissionType)) { + case Bool.kFalse: + newPermissionValue = Bool.kTrue; + break; + case Bool.kTrue: + newPermissionValue = Bool.kFalse; + break; + default: + assertNotReached(); + } + + return app_management.util.createPermission( + PwaPermissionType[permissionType], PermissionValueType.kBool, + newPermissionValue); + }, + + /** + * @param {App} app + * @param {string} permissionType + * @private + * @return {!Permission} + */ + getNewPermissionTriState_: function(app, permissionType) { + let newPermissionValue; + + switch (app_management.util.getPermissionValue(app, permissionType)) { + case TriState.kBlock: + newPermissionValue = TriState.kAllow; + break; + case TriState.kAsk: + newPermissionValue = TriState.kAllow; + break; + case TriState.kAllow: + // TODO(rekanorman): Eventually TriState.kAsk, but currently changing a + // permission to kAsk then opening the site settings page for the app + // produces the error: + // "Only extensions or enterprise policy can change the setting to ASK." + newPermissionValue = TriState.kBlock; + break; + default: + assertNotReached(); + } + + return app_management.util.createPermission( + PwaPermissionType[permissionType], PermissionValueType.kTriState, + newPermissionValue); + }, +});
diff --git a/chrome/browser/resources/app_management/util.js b/chrome/browser/resources/app_management/util.js index c7a43c97..c30185d 100644 --- a/chrome/browser/resources/app_management/util.js +++ b/chrome/browser/resources/app_management/util.js
@@ -16,7 +16,7 @@ currentPage: { pageType: PageType.MAIN, selectedAppId: null, - } + }, }; } @@ -57,6 +57,91 @@ } /** + * Returns true if the current page is notification view. + * @param {AppManagementPageState} state + * @return {boolean} + */ + function isNotificationsViewSelected(state) { + return state.currentPage.pageType === PageType.NOTIFICATIONS; + } + + /** + * @return {!Array<Array<App>>} + */ + function splitByNotificationPermission() { + const apps = Object.values(app_management.Store.getInstance().data.apps); + const notificationsAllowed = []; + const notificationsBlocked = []; + for (const app of apps) { + // Chrome app's notification permission cannot be changed and therefore + // are not included in notification view + if (app.type === AppType.kExtension) { + continue; + } + // TODO(rekanorman) Branch on different app type in the future. + if (getPermissionValueBool(app, 'CONTENT_SETTINGS_TYPE_NOTIFICATIONS')) { + notificationsAllowed.push(app); + } else { + notificationsBlocked.push(app); + } + } + return [notificationsAllowed, notificationsBlocked]; + } + + /** + * @param {App} app + * @param {string} permissionType + * @return {boolean} + */ + function getPermissionValueBool(app, permissionType) { + if (getPermissionValueType(app) === PermissionValueType.kBool && + getPermissionValue(app, permissionType) === Bool.kTrue) { + return true; + } + if (getPermissionValueType(app) === PermissionValueType.kTriState && + getPermissionValue(app, permissionType) === TriState.kAllow) { + return true; + } + return false; + } + + /** + * @param {App} app + * @return {number} + * @private + */ + function getPermissionValueType(app) { + // TODO(rekanorman): Change to a suitable conditional statement once the + // PermissionValueType corresponding to each AppType is known. + return PermissionValueType.kTriState; + } + + + /** + * @param {App} app + * @param {string} permissionType + * @return {?number} + * @private + */ + function getPermissionValue(app, permissionType) { + if (!app) { + return null; + } + + // TODO(rekanorman): Remove once permissions are implemented for all + // app types. + if (Object.keys(app.permissions).length === 0) { + return null; + } + + // TODO(rekanorman): Make generic once permissions are implemented for + // other app types. + return /** @type Object<number, Permission> */ ( + app.permissions)[PwaPermissionType[permissionType]] + .value; + } + + /** * If there is no selected app, returns undefined so that Polymer bindings * won't be updated. * @param {AppManagementPageState} state @@ -74,6 +159,11 @@ createInitialState: createInitialState, createPermission: createPermission, getAppIcon: getAppIcon, + getPermissionValue: getPermissionValue, + getPermissionValueBool: getPermissionValueBool, + getPermissionValueType: getPermissionValueType, getSelectedApp: getSelectedApp, + isNotificationsViewSelected: isNotificationsViewSelected, + splitByNotificationPermission: splitByNotificationPermission, }; });
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html index 85072d8..452cef16 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -79,7 +79,9 @@ <!-- Title section: Icon + name + connection state. --> <div class="section first"> <div class="start layout horizontal center"> - <cr-network-icon network-state="[[networkProperties]]"> + <cr-network-icon + show-technology-badge="[[showTechnologyBadge_]]" + network-state="[[networkProperties]]"> </cr-network-icon> <div id="networkName" class="title"> [[getNameText_(networkProperties)]]
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js index 1a2c34e6..5ecdaa20 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
@@ -30,6 +30,18 @@ type: Object, value: chrome.networkingPrivate, }, + + /** + * Whether to show technology badge on mobile network icons. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + }, }, /**
diff --git a/chrome/browser/resources/chromeos/login/network_select_login.html b/chrome/browser/resources/chromeos/login/network_select_login.html index f9fc9257..a6fc8296 100644 --- a/chrome/browser/resources/chromeos/login/network_select_login.html +++ b/chrome/browser/resources/chromeos/login/network_select_login.html
@@ -14,15 +14,15 @@ } </style> <template> - <cr-network-select id="networkSelect" + <cr-network-select id="networkSelect" class="focus-on-show" + custom-items="[[getNetworkCustomItems_(isConnected)]]" on-default-network-changed="onDefaultNetworkChanged_" on-network-connect-changed="onNetworkConnectChanged_" on-network-list-changed="onNetworkListChanged_" on-network-item-selected="onNetworkListNetworkItemSelected_" on-custom-item-selected="onNetworkListCustomItemSelected_" - custom-items="[[getNetworkCustomItems_(isConnected)]]" no-bottom-scroll-border="[[noBottomScrollBorder]]" - class="focus-on-show"> + show-technology-badge="[[showTechnologyBadge_]]"> </cr-network-select> </template> </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/network_select_login.js b/chrome/browser/resources/chromeos/login/network_select_login.js index 2c0cd8a..329152a1 100644 --- a/chrome/browser/resources/chromeos/login/network_select_login.js +++ b/chrome/browser/resources/chromeos/login/network_select_login.js
@@ -39,6 +39,18 @@ type: Boolean, value: false, }, + + /** + * Whether to show technology badge on mobile network icons. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + } }, /**
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/internet_page/internet_detail_page.html index 82b0c6e..a9ed2ea0 100644 --- a/chrome/browser/resources/settings/internet_page/internet_detail_page.html +++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.html
@@ -68,7 +68,9 @@ <!-- Title section: Icon + name + connection state. --> <div id="titleDiv" class="settings-box first"> <div class="start layout horizontal center"> - <cr-network-icon network-state="[[networkProperties]]"> + <cr-network-icon + show-technology-badge="[[showTechnologyBadge_]]" + network-state="[[networkProperties]]"> </cr-network-icon> <div id="networkState" class="title settings-box-text" connected$="[[isConnectedState_(networkProperties)]]"
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js index 3ef1fd6..3af1e32 100644 --- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js +++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -143,6 +143,18 @@ value: '', }, + /** + * Whether to show technology badge on mobile network icons. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + }, + /** @private */ advancedExpanded_: Boolean,
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chrome/browser/resources/settings/internet_page/internet_subpage.html index 23c8468..ce1d5ac 100644 --- a/chrome/browser/resources/settings/internet_page/internet_subpage.html +++ b/chrome/browser/resources/settings/internet_page/internet_subpage.html
@@ -125,6 +125,7 @@ <template is="dom-if" if="[[shouldShowNetworkList_(networkStateList_)]]"> <cr-network-list id="networkList" show-buttons + show-technology-badge="[[showTechnologyBadge_]]" networks="[[networkStateList_]]" on-selected="onNetworkSelected_"> </cr-network-list>
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.js b/chrome/browser/resources/settings/internet_page/internet_subpage.js index e74c0f8..4ca6059 100644 --- a/chrome/browser/resources/settings/internet_page/internet_subpage.js +++ b/chrome/browser/resources/settings/internet_page/internet_subpage.js
@@ -111,6 +111,18 @@ return []; }, }, + + /** + * Whether to show technology badge on mobile network icons. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + }, }, listeners: {
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.html b/chrome/browser/resources/settings/internet_page/network_summary_item.html index da42463..1dbb066 100644 --- a/chrome/browser/resources/settings/internet_page/network_summary_item.html +++ b/chrome/browser/resources/settings/internet_page/network_summary_item.html
@@ -45,7 +45,9 @@ <div actionable class="flex layout horizontal center" on-click="onShowDetailsTap_"> <div id="details" no-flex$="[[showSimInfo_(deviceState)]]"> - <cr-network-icon network-state="[[activeNetworkState]]" + <cr-network-icon + show-technology-badge="[[showTechnologyBadge_]]" + network-state="[[activeNetworkState]]" device-state="[[deviceState]]"> </cr-network-icon> <div class="flex settings-box-text">
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js index 4720c74..4821799 100644 --- a/chrome/browser/resources/settings/internet_page/network_summary_item.js +++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -59,6 +59,18 @@ * @type {string|undefined} */ networkTitleText: String, + + /** + * Whether to show technology badge on mobile network icon. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + }, }, /**
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html index 6f6e258..42431eb 100644 --- a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html +++ b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.html
@@ -19,6 +19,7 @@ subpage-route-url-search-params= "[[getTetherNetworkUrlSearchParams_()]]"> <cr-network-icon slot="icon" + show-technology-badge="[[showTechnologyBadge_]]" network-state="[[activeNetworkState_]]" device-state="[[deviceState_]]"> </cr-network-icon>
diff --git a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js index aef05a82..a5c84407 100644 --- a/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js +++ b/chrome/browser/resources/settings/multidevice_page/multidevice_tether_item.js
@@ -48,6 +48,18 @@ type: Object, value: settings.routes, }, + + /** + * Whether to show technology badge on mobile network icon. + * @private + */ + showTechnologyBadge_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('showTechnologyBadge') && + loadTimeData.getBoolean('showTechnologyBadge'); + } + }, }, listeners: {
diff --git a/chrome/browser/ssl/ssl_client_auth_metrics.cc b/chrome/browser/ssl/ssl_client_auth_metrics.cc new file mode 100644 index 0000000..8e01da0f --- /dev/null +++ b/chrome/browser/ssl/ssl_client_auth_metrics.cc
@@ -0,0 +1,11 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ssl/ssl_client_auth_metrics.h" + +#include "base/metrics/histogram_macros.h" + +void LogClientAuthResult(ClientCertSelectionResult result) { + UMA_HISTOGRAM_ENUMERATION(kClientCertSelectHistogramName, result); +}
diff --git a/chrome/browser/ssl/ssl_client_auth_metrics.h b/chrome/browser/ssl/ssl_client_auth_metrics.h new file mode 100644 index 0000000..b2d8a6d --- /dev/null +++ b/chrome/browser/ssl/ssl_client_auth_metrics.h
@@ -0,0 +1,25 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SSL_SSL_CLIENT_AUTH_METRICS_H_ +#define CHROME_BROWSER_SSL_SSL_CLIENT_AUTH_METRICS_H_ + +const char kClientCertSelectHistogramName[] = + "Security.ClientAuth.CertificateSelectionSource"; + +// These values are logged to UMA. Entries should not be renumbered and numeric +// values should never be reused. Please keep in sync with +// "ClientCertSelectionResult" in src/tools/metrics/histograms/enums.xml. +enum class ClientCertSelectionResult { + kUserSelect = 0, + kUserCancel = 1, + kUserCloseTab = 2, + kAutoSelect = 3, + kNoSelectionAllowed = 4, + kMaxValue = kNoSelectionAllowed, +}; + +void LogClientAuthResult(ClientCertSelectionResult result); + +#endif // CHROME_BROWSER_SSL_SSL_CLIENT_AUTH_METRICS_H_
diff --git a/chrome/browser/ssl/ssl_client_auth_observer.cc b/chrome/browser/ssl/ssl_client_auth_observer.cc index 21a7b22..97b5424 100644 --- a/chrome/browser/ssl/ssl_client_auth_observer.cc +++ b/chrome/browser/ssl/ssl_client_auth_observer.cc
@@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/no_destructor.h" +#include "chrome/browser/ssl/ssl_client_auth_metrics.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/client_certificate_delegate.h" #include "net/cert/x509_certificate.h" @@ -31,6 +32,14 @@ if (!delegate_) return; + // CertificateSelected is called with a valid delegate any time that the + // selector is explicitly closed by the user. If the user closes the entire + // tab, CancelCertificateSelection() is called first, removing the delegate. + if (certificate) + LogClientAuthResult(ClientCertSelectionResult::kUserSelect); + else + LogClientAuthResult(ClientCertSelectionResult::kUserCancel); + // Stop listening now that the delegate has been resolved. This is also to // avoid getting a self-notification. StopObserving(); @@ -51,6 +60,10 @@ if (!delegate_) return; + // This code is only reached when the selector's tab is closed-- cancelling + // the selection box calls CertificateSelected(nullptr, nullptr) first. + LogClientAuthResult(ClientCertSelectionResult::kUserCloseTab); + // Stop observing now that the delegate has been resolved. StopObserving(); delegate_.reset();
diff --git a/chrome/browser/ui/ash/assistant/assistant_client.cc b/chrome/browser/ui/ash/assistant/assistant_client.cc index cc3f130..492d1c4 100644 --- a/chrome/browser/ui/ash/assistant/assistant_client.cc +++ b/chrome/browser/ui/ash/assistant/assistant_client.cc
@@ -66,6 +66,10 @@ assistant_setup_ = std::make_unique<AssistantSetup>(connector); } +void AssistantClient::MaybeStartAssistantOptInFlow() { + assistant_setup_->MaybeStartAssistantOptInFlow(); +} + void AssistantClient::OnAssistantStatusChanged(bool running) { // |running| means assistent mojom service is running. This maps to // |STOPPED| and |NOT_READY|. |RUNNING| maps to UI is shown and an assistant
diff --git a/chrome/browser/ui/ash/assistant/assistant_client.h b/chrome/browser/ui/ash/assistant/assistant_client.h index a3ac07e..eef5571 100644 --- a/chrome/browser/ui/ash/assistant/assistant_client.h +++ b/chrome/browser/ui/ash/assistant/assistant_client.h
@@ -25,6 +25,7 @@ ~AssistantClient() override; void MaybeInit(Profile* profile); + void MaybeStartAssistantOptInFlow(); // assistant::mojom::Client overrides: void OnAssistantStatusChanged(bool running) override;
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.cc b/chrome/browser/ui/ash/assistant/assistant_setup.cc index 719bdcc..68f5ed5 100644 --- a/chrome/browser/ui/ash/assistant/assistant_setup.cc +++ b/chrome/browser/ui/ash/assistant/assistant_setup.cc
@@ -12,8 +12,10 @@ #include "ash/public/interfaces/assistant_controller.mojom.h" #include "ash/public/interfaces/constants.mojom.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/metrics/histogram_macros.h" #include "base/strings/utf_string_conversions.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "chrome/browser/chromeos/login/ui/login_display_host.h" #include "chrome/browser/notifications/notification_display_service.h" #include "chrome/browser/profiles/profile.h" @@ -82,7 +84,7 @@ } // namespace AssistantSetup::AssistantSetup(service_manager::Connector* connector) - : connector_(connector), binding_(this) { + : connector_(connector), binding_(this), weak_factory_(this) { // Bind to the AssistantSetupController in ash. ash::mojom::AssistantSetupControllerPtr setup_controller; connector_->BindInterface(ash::mojom::kServiceName, &setup_controller); @@ -100,8 +102,10 @@ void AssistantSetup::StartAssistantOptInFlow( ash::mojom::FlowType type, StartAssistantOptInFlowCallback callback) { - if (chromeos::AssistantOptInDialog::IsActive()) + if (chromeos::AssistantOptInDialog::IsActive()) { + std::move(callback).Run(false); return; + } chromeos::AssistantOptInDialog::Show(type, std::move(callback)); } @@ -210,3 +214,16 @@ LOG(ERROR) << "Invalid activity control consent status."; } } + +void AssistantSetup::MaybeStartAssistantOptInFlow() { + auto* pref_service = ProfileManager::GetActiveUserProfile()->GetPrefs(); + DCHECK(pref_service); + if (!pref_service->GetUserPrefValue( + arc::prefs::kVoiceInteractionActivityControlAccepted)) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&AssistantSetup::StartAssistantOptInFlow, + weak_factory_.GetWeakPtr(), + ash::mojom::FlowType::CONSENT_FLOW, + base::DoNothing::Once<bool>())); + } +}
diff --git a/chrome/browser/ui/ash/assistant/assistant_setup.h b/chrome/browser/ui/ash/assistant/assistant_setup.h index a19653a..1a16670b 100644 --- a/chrome/browser/ui/ash/assistant/assistant_setup.h +++ b/chrome/browser/ui/ash/assistant/assistant_setup.h
@@ -7,6 +7,7 @@ #include "ash/public/interfaces/assistant_setup.mojom.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h" #include "chromeos/services/assistant/public/mojom/settings.mojom.h" #include "mojo/public/cpp/bindings/binding.h" @@ -27,6 +28,10 @@ ash::mojom::FlowType type, StartAssistantOptInFlowCallback callback) override; + // If prefs::kVoiceInteractionActivityControlAccepted is nullptr, means the + // pref is not set by user. Therefore we need to start OOBE. + void MaybeStartAssistantOptInFlow(); + private: // arc::VoiceInteractionControllerClient::Observer overrides void OnStateChanged(ash::mojom::VoiceInteractionState state) override; @@ -38,6 +43,8 @@ chromeos::assistant::mojom::AssistantSettingsManagerPtr settings_manager_; mojo::Binding<ash::mojom::AssistantSetup> binding_; + base::WeakPtrFactory<AssistantSetup> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(AssistantSetup); };
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc index c779638..e62c500 100644 --- a/chrome/browser/ui/ash/session_controller_client.cc +++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -472,9 +472,11 @@ #if BUILDFLAG(ENABLE_CROS_ASSISTANT) // Assistant is initialized only once when primary user logs in. + // Initialize Assistant when browser process restarts. if (chromeos::switches::IsAssistantEnabled()) { AssistantClient::Get()->MaybeInit( ProfileManager::GetPrimaryUserProfile()); + AssistantClient::Get()->MaybeStartAssistantOptInFlow(); } #endif }
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn index 3a53a45..407f90ce 100644 --- a/chrome/browser/ui/libgtkui/BUILD.gn +++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -5,10 +5,11 @@ assert(is_linux, "This file should only be referenced on Linux") import("//build/config/features.gni") +import("//build/config/jumbo.gni") import("//build/config/linux/gtk/gtk.gni") import("//printing/buildflags/buildflags.gni") -component("libgtkui") { +jumbo_component("libgtkui") { sources = [ "app_indicator_icon.cc", "app_indicator_icon.h",
diff --git a/chrome/browser/ui/libgtkui/app_indicator_icon.cc b/chrome/browser/ui/libgtkui/app_indicator_icon.cc index 266ae5d..3e1aa95 100644 --- a/chrome/browser/ui/libgtkui/app_indicator_icon.cc +++ b/chrome/browser/ui/libgtkui/app_indicator_icon.cc
@@ -83,7 +83,7 @@ app_indicator_set_icon_theme_path_func app_indicator_set_icon_theme_path = nullptr; -void EnsureMethodsLoaded() { +void EnsureLibAppIndicatorLoaded() { if (g_attempted_load) return; @@ -160,7 +160,7 @@ std::unique_ptr<base::Environment> env(base::Environment::Create()); desktop_env_ = base::nix::GetDesktopEnvironment(env.get()); - EnsureMethodsLoaded(); + EnsureLibAppIndicatorLoaded(); tool_tip_ = base::UTF16ToUTF8(tool_tip); SetImage(image); } @@ -176,7 +176,7 @@ // static bool AppIndicatorIcon::CouldOpen() { - EnsureMethodsLoaded(); + EnsureLibAppIndicatorLoaded(); return g_opened; }
diff --git a/chrome/browser/ui/libgtkui/gtk_util.cc b/chrome/browser/ui/libgtkui/gtk_util.cc index 42b8ac9..bb6102e 100644 --- a/chrome/browser/ui/libgtkui/gtk_util.cc +++ b/chrome/browser/ui/libgtkui/gtk_util.cc
@@ -589,4 +589,15 @@ return surface.GetAveragePixelValue(false); } +std::string GetGtkSettingsStringProperty(GtkSettings* settings, + const gchar* prop_name) { + GValue layout = G_VALUE_INIT; + g_value_init(&layout, G_TYPE_STRING); + g_object_get_property(G_OBJECT(settings), prop_name, &layout); + DCHECK(G_VALUE_HOLDS_STRING(&layout)); + std::string prop_value(g_value_get_string(&layout)); + g_value_unset(&layout); + return prop_value; +} + } // namespace libgtkui
diff --git a/chrome/browser/ui/libgtkui/gtk_util.h b/chrome/browser/ui/libgtkui/gtk_util.h index 32b0a39..adea4a8 100644 --- a/chrome/browser/ui/libgtkui/gtk_util.h +++ b/chrome/browser/ui/libgtkui/gtk_util.h
@@ -184,6 +184,9 @@ // Get the color of the GtkSeparator specified by |css_selector|. SkColor GetSeparatorColor(const std::string& css_selector); +// Get a GtkSettings property as a C++ string. +std::string GetGtkSettingsStringProperty(GtkSettings* settings, + const gchar* prop_name); } // namespace libgtkui #endif // CHROME_BROWSER_UI_LIBGTKUI_GTK_UTIL_H_
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc index ae8d88d..4e6196d 100644 --- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc +++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -25,17 +25,6 @@ BG_RENDER_RECURSIVE, }; -std::string GetGtkSettingsStringProperty(GtkSettings* settings, - const gchar* prop_name) { - GValue layout = G_VALUE_INIT; - g_value_init(&layout, G_TYPE_STRING); - g_object_get_property(G_OBJECT(settings), prop_name, &layout); - DCHECK(G_VALUE_HOLDS_STRING(&layout)); - std::string prop_value(g_value_get_string(&layout)); - g_value_unset(&layout); - return prop_value; -} - ScopedStyleContext GetTooltipContext() { return AppendCssNodeToStyleContext( nullptr, GtkVersionCheck(3, 20) ? "#tooltip.background"
diff --git a/chrome/browser/ui/libgtkui/settings_provider_gtk.cc b/chrome/browser/ui/libgtkui/settings_provider_gtk.cc index 28ba3fb8..3b95bc9 100644 --- a/chrome/browser/ui/libgtkui/settings_provider_gtk.cc +++ b/chrome/browser/ui/libgtkui/settings_provider_gtk.cc
@@ -14,17 +14,6 @@ const char kDefaultGtkLayout[] = "menu:minimize,maximize,close"; -std::string GetGtkSettingsStringProperty(GtkSettings* settings, - const gchar* prop_name) { - GValue layout = G_VALUE_INIT; - g_value_init(&layout, G_TYPE_STRING); - g_object_get_property(G_OBJECT(settings), prop_name, &layout); - DCHECK(G_VALUE_HOLDS_STRING(&layout)); - std::string prop_value(g_value_get_string(&layout)); - g_value_unset(&layout); - return prop_value; -} - std::string GetDecorationLayoutFromGtkWindow() { #if GTK_CHECK_VERSION(3, 90, 0) NOTREACHED();
diff --git a/chrome/browser/ui/libgtkui/unity_service.cc b/chrome/browser/ui/libgtkui/unity_service.cc index 9903568..4678006 100644 --- a/chrome/browser/ui/libgtkui/unity_service.cc +++ b/chrome/browser/ui/libgtkui/unity_service.cc
@@ -51,7 +51,7 @@ unity_launcher_entry_set_progress_visible_func entry_set_progress_visible = nullptr; -void EnsureMethodsLoaded() { +void EnsureLibUnityLoaded() { using base::nix::GetDesktopEnvironment; if (attempted_load) @@ -119,7 +119,7 @@ namespace unity { bool IsRunning() { - EnsureMethodsLoaded(); + EnsureLibUnityLoaded(); if (inspector && get_unity_running) return get_unity_running(inspector); @@ -127,7 +127,7 @@ } void SetDownloadCount(int count) { - EnsureMethodsLoaded(); + EnsureLibUnityLoaded(); if (chrome_entry && entry_set_count && entry_set_count_visible) { entry_set_count(chrome_entry, count); entry_set_count_visible(chrome_entry, count != 0); @@ -135,7 +135,7 @@ } void SetProgressFraction(float percentage) { - EnsureMethodsLoaded(); + EnsureLibUnityLoaded(); if (chrome_entry && entry_set_progress && entry_set_progress_visible) { entry_set_progress(chrome_entry, percentage); entry_set_progress_visible(chrome_entry,
diff --git a/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc b/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc index 5e60a010..b4f8918 100644 --- a/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc +++ b/chrome/browser/ui/search/local_ntp_doodle_browsertest.cc
@@ -122,6 +122,26 @@ return base::nullopt; } + void TeardownNavigatorTest(content::WebContents* tab) { + ASSERT_TRUE(content::ExecuteScript(tab, "window.navigator = navigatorOld")); + } + + void SetupBeaconTest(content::WebContents* tab) { + ASSERT_TRUE(content::ExecuteScript(tab, + "var navigatorOld = window.navigator; " + "window.navigator = {};" + "window.navigator.sendBeacon = " + "(url) => { sentbeacon = url };")); + } + + base::Optional<std::string> GetBeaconURL(content::WebContents* tab) { + std::string target_url; + if (instant_test_utils::GetStringFromJS(tab, "sentbeacon", &target_url)) { + return target_url; + } + return base::nullopt; + } + bool ElementExists(content::WebContents* tab, const std::string& id) { return ExecuteBooleanJS( tab, base::StringPrintf("!!document.getElementById(%s)", @@ -939,6 +959,130 @@ EXPECT_EQ("https://g.co/", short_link); } +IN_PROC_BROWSER_TEST_F(LocalNTPDoodleTest, ShouldLogShareClicksNoEventId) { + EncodedLogo cached_logo; + cached_logo.encoded_image = MakeRefPtr(kCachedB64); + cached_logo.metadata.mime_type = "image/png"; + cached_logo.metadata.on_click_url = + GURL("https://www.chrotmium.org/?ct=test"); + cached_logo.metadata.alt_text = "Chromium"; + cached_logo.metadata.short_link = GURL("https://g.co"); + cached_logo.metadata.share_button_x = 12; + cached_logo.metadata.share_button_y = 36; + cached_logo.metadata.share_button_opacity = 0.8; + cached_logo.metadata.share_button_icon = "sbimg"; + cached_logo.metadata.share_button_bg = "#ffff00"; + + EXPECT_CALL(*logo_service(), GetLogoPtr(_)) + .WillRepeatedly(DoAll( + ReturnCachedLogo(LogoCallbackReason::DETERMINED, cached_logo), + ReturnFreshLogo(LogoCallbackReason::REVALIDATED, base::nullopt))); + + // Open a new blank tab, then go to NTP and listen for console messages. + content::WebContents* active_tab = + local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank")); + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL)); + + // Replace window.open so we stay in the same tab. + SetupWindowOpenTest(active_tab); + SetupBeaconTest(active_tab); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsb').click();")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-fbb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204" + "?atyp=i&ct=doodle&cad=sh%2C2%2Cct%3Atest&ntp=1")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-twb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204" + "?atyp=i&ct=doodle&cad=sh%2C3%2Cct%3Atest&ntp=1")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-emb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204" + "?atyp=i&ct=doodle&cad=sh%2C5%2Cct%3Atest&ntp=1")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-copy').click();")); + std::string short_link; + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204" + "?atyp=i&ct=doodle&cad=sh%2C6%2Cct%3Atest&ntp=1")); +} + +IN_PROC_BROWSER_TEST_F(LocalNTPDoodleTest, ShouldLogShareClicksWithEventId) { + EncodedLogo cached_logo; + cached_logo.encoded_image = MakeRefPtr(kCachedB64); + cached_logo.metadata.mime_type = "image/png"; + cached_logo.metadata.on_click_url = + GURL("https://www.chrotmium.org/?ct=test"); + cached_logo.metadata.alt_text = "Chromium"; + cached_logo.metadata.short_link = GURL("https://g.co"); + cached_logo.metadata.share_button_x = 12; + cached_logo.metadata.share_button_y = 36; + cached_logo.metadata.share_button_opacity = 0.8; + cached_logo.metadata.share_button_icon = "sbimg"; + cached_logo.metadata.share_button_bg = "#ffff00"; + + EXPECT_CALL(*logo_service(), GetLogoPtr(_)) + .WillRepeatedly(DoAll( + ReturnCachedLogo(LogoCallbackReason::DETERMINED, cached_logo), + ReturnFreshLogo(LogoCallbackReason::REVALIDATED, base::nullopt))); + + // Open a new blank tab, then go to NTP and listen for console messages. + content::WebContents* active_tab = + local_ntp_test_utils::OpenNewTab(browser(), GURL("about:blank")); + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL)); + + // Replace window.open so we stay in the same tab. + SetupWindowOpenTest(active_tab); + SetupBeaconTest(active_tab); + + ASSERT_TRUE(content::ExecuteScript(active_tab, "doodles.ei = 'test_ei';")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsb').click();")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-fbb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204?atyp=i" + "&ct=doodle&cad=sh%2C2%2Cct%3Atest&ntp=1&ei=test_ei")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-twb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204?atyp=i" + "&ct=doodle&cad=sh%2C3%2Cct%3Atest&ntp=1&ei=test_ei")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-emb').click();")); + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204?atyp=i" + "&ct=doodle&cad=sh%2C5%2Cct%3Atest&ntp=1&ei=test_ei")); + + ASSERT_TRUE(content::ExecuteScript( + active_tab, "document.getElementById('ddlsd-copy').click();")); + std::string short_link; + EXPECT_THAT( + GetBeaconURL(active_tab), + Eq<std::string>("https://www.google.com/gen_204?atyp=i" + "&ct=doodle&cad=sh%2C6%2Cct%3Atest&ntp=1&ei=test_ei")); +} + IN_PROC_BROWSER_TEST_F(LocalNTPDoodleTest, ShouldAnimateLogoWhenClicked) { EncodedLogo cached_logo; cached_logo.encoded_image = MakeRefPtr(kCachedB64);
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc index b4ea285..209fd1b 100644 --- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc +++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -148,17 +148,13 @@ } void PaymentRequestBrowserTestBase::OnHasEnrolledInstrumentCalled() { - // TODO(https://crbug.com/915907): rename enum to HAS_ENROLLED_INSTRUMENT - // version when new CanMakePayment behavior is implemented. if (event_waiter_) - event_waiter_->OnEvent(DialogEvent::CAN_MAKE_PAYMENT_CALLED); + event_waiter_->OnEvent(DialogEvent::HAS_ENROLLED_INSTRUMENT_CALLED); } void PaymentRequestBrowserTestBase::OnHasEnrolledInstrumentReturned() { - // TODO(https://crbug.com/915907): rename enum to HAS_ENROLLED_INSTRUMENT - // version when new CanMakePayment behavior is implemented. if (event_waiter_) - event_waiter_->OnEvent(DialogEvent::CAN_MAKE_PAYMENT_RETURNED); + event_waiter_->OnEvent(DialogEvent::HAS_ENROLLED_INSTRUMENT_RETURNED); } void PaymentRequestBrowserTestBase::OnNotSupportedError() {
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc index 8d80273..f4ede9f 100644 --- a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc +++ b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc
@@ -6,8 +6,10 @@ #include "base/files/file_path.h" #include "base/synchronization/waitable_event.h" #include "base/task/post_task.h" +#include "base/test/metrics/histogram_tester.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/ssl_client_auth_metrics.h" #include "chrome/browser/ssl/ssl_client_auth_requestor_mock.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" @@ -293,15 +295,20 @@ #define MAYBE_Escape Escape #endif IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, MAYBE_Escape) { + base::HistogramTester histograms; EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(nullptr, nullptr)); EXPECT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_ESCAPE, false, false, false, false)); + histograms.ExpectUniqueSample(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserCancel, 1); + Mock::VerifyAndClear(auth_requestor_.get()); } IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectDefault) { + base::HistogramTester histograms; EXPECT_CALL(*auth_requestor_.get(), CertificateSelected(cert_identity_1_->certificate(), cert_identity_1_->ssl_private_key())); @@ -309,6 +316,21 @@ EXPECT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_RETURN, false, false, false, false)); + histograms.ExpectUniqueSample(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserSelect, 1); + + Mock::VerifyAndClear(auth_requestor_.get()); +} + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, CloseTab) { + base::HistogramTester histograms; + EXPECT_CALL(*auth_requestor_.get(), CancelCertificateSelection()); + + browser()->tab_strip_model()->CloseAllTabs(); + + histograms.ExpectBucketCount(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserCloseTab, 1); + Mock::VerifyAndClear(auth_requestor_.get()); }
diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm b/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm index 6e7c86a..655c578 100644 --- a/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm +++ b/chrome/browser/ui/views/ssl_client_certificate_selector_mac_browsertest.mm
@@ -12,6 +12,8 @@ #include "base/bind_helpers.h" #import "base/mac/mac_util.h" #include "base/macros.h" +#include "base/test/metrics/histogram_tester.h" +#include "chrome/browser/ssl/ssl_client_auth_metrics.h" #include "chrome/browser/ssl/ssl_client_certificate_selector.h" #include "chrome/browser/ssl/ssl_client_certificate_selector_test.h" #include "chrome/browser/ui/browser.h" @@ -130,6 +132,7 @@ } IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Basic) { + base::HistogramTester histograms; content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); WebContentsModalDialogManager* web_contents_modal_dialog_manager = @@ -150,11 +153,15 @@ base::RunLoop().RunUntilIdle(); EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive()); + histograms.ExpectUniqueSample(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserCloseTab, 1); + EXPECT_TRUE(results.destroyed); EXPECT_FALSE(results.continue_with_certificate_called); } IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Cancel) { + base::HistogramTester histograms; content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); WebContentsModalDialogManager* web_contents_modal_dialog_manager = @@ -175,6 +182,9 @@ base::RunLoop().RunUntilIdle(); EXPECT_FALSE(web_contents_modal_dialog_manager->IsDialogActive()); + histograms.ExpectUniqueSample(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserCancel, 1); + // ContinueWithCertificate(nullptr, nullptr) should have been called. EXPECT_TRUE(results.destroyed); EXPECT_TRUE(results.continue_with_certificate_called); @@ -183,6 +193,7 @@ } IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMacTest, Accept) { + base::HistogramTester histograms; content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); WebContentsModalDialogManager* web_contents_modal_dialog_manager = @@ -209,6 +220,9 @@ EXPECT_EQ(client_cert1_, results.cert); ASSERT_TRUE(results.key); + histograms.ExpectUniqueSample(kClientCertSelectHistogramName, + ClientCertSelectionResult::kUserSelect, 1); + // The test keys are RSA keys. EXPECT_EQ(net::SSLPrivateKey::DefaultAlgorithmPreferences( EVP_PKEY_RSA, base::mac::IsAtLeastOS10_13()),
diff --git a/chrome/browser/ui/views/sync/dice_bubble_sync_promo_view.cc b/chrome/browser/ui/views/sync/dice_bubble_sync_promo_view.cc index e6f2e948..c35a259 100644 --- a/chrome/browser/ui/views/sync/dice_bubble_sync_promo_view.cc +++ b/chrome/browser/ui/views/sync/dice_bubble_sync_promo_view.cc
@@ -11,12 +11,10 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" #include "chrome/browser/signin/account_consistency_mode_manager.h" -#include "chrome/browser/signin/account_tracker_service_factory.h" #include "chrome/browser/signin/signin_ui_util.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_typography.h" #include "chrome/browser/ui/views/sync/dice_signin_button_view.h" -#include "components/signin/core/browser/account_tracker_service.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/controls/label.h"
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc index 3fbc518..a803b7ce 100644 --- a/chrome/browser/ui/webui/app_management/app_management_ui.cc +++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -105,6 +105,10 @@ IDR_APP_MANAGEMENT_PERMISSION_ITEM_HTML); source->AddResourcePath("permission_item.js", IDR_APP_MANAGEMENT_PERMISSION_ITEM_JS); + source->AddResourcePath("permission_toggle.html", + IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_HTML); + source->AddResourcePath("permission_toggle.js", + IDR_APP_MANAGEMENT_PERMISSION_TOGGLE_JS); source->AddResourcePath("pwa_permission_view.html", IDR_APP_MANAGEMENT_PWA_PERMISSION_VIEW_HTML); source->AddResourcePath("pwa_permission_view.js",
diff --git a/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc b/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc index 11226162..c29177b 100644 --- a/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc +++ b/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h" +#include "ash/public/cpp/ash_features.h" #include "base/json/json_writer.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" @@ -136,7 +137,8 @@ : ui::WebDialogUI(web_ui) { content::WebUIDataSource* source = content::WebUIDataSource::Create( chrome::kChromeUIInternetDetailDialogHost); - + source->AddBoolean("showTechnologyBadge", + !ash::features::IsSeparateNetworkIconsEnabled()); AddInternetStrings(source); source->AddLocalizedString("title", IDS_SETTINGS_INTERNET_DETAIL); source->SetJsonPath("strings.js");
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc index 1c5f2ef..3ef92b7 100644 --- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -6,6 +6,7 @@ #include <type_traits> +#include "ash/public/cpp/ash_features.h" #include "ash/public/interfaces/constants.mojom.h" #include "ash/public/interfaces/event_rewriter_controller.mojom.h" #include "ash/shell.h" @@ -186,6 +187,8 @@ base::Value(TabletModeClient::Get()->tablet_mode_enabled())); dict->SetKey("isDemoModeEnabled", base::Value(DemoSetupController::IsDemoModeAllowed())); + dict->SetKey("showTechnologyBadge", + base::Value(!ash::features::IsSeparateNetworkIconsEnabled())); } void CoreOobeHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc index 6e577089..3f23bea8 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -324,6 +324,8 @@ html_source->AddBoolean( "lockScreenHideSensitiveNotificationsSupported", ash::features::IsLockScreenHideSensitiveNotificationsSupported()); + html_source->AddBoolean("showTechnologyBadge", + !ash::features::IsSeparateNetworkIconsEnabled()); html_source->AddBoolean("hasInternalStylus", ash::stylus_utils::HasInternalStylus());
diff --git a/chrome/browser/web_applications/bookmark_apps/BUILD.gn b/chrome/browser/web_applications/bookmark_apps/BUILD.gn index a0722b3..b8cfbab 100644 --- a/chrome/browser/web_applications/bookmark_apps/BUILD.gn +++ b/chrome/browser/web_applications/bookmark_apps/BUILD.gn
@@ -24,6 +24,26 @@ ] } +source_set("test_support") { + testonly = true + + sources = [ + "test_web_app_provider.cc", + "test_web_app_provider.h", + ] + + deps = [ + ":bookmark_apps", + "//base", + "//chrome/browser", + "//chrome/browser/web_applications", + "//chrome/browser/web_applications:web_app_group", + "//chrome/browser/web_applications:web_applications_on_extensions", + "//chrome/browser/web_applications/components", + "//components/keyed_service/content", + ] +} + source_set("unit_tests") { testonly = true @@ -35,6 +55,7 @@ deps = [ ":bookmark_apps", + ":test_support", "//base", "//chrome/browser", "//chrome/browser/web_applications", @@ -66,12 +87,11 @@ sources = [ "system_web_app_manager_browsertest.cc", "system_web_app_manager_browsertest.h", - "test_web_app_provider.cc", - "test_web_app_provider.h", ] deps = [ ":bookmark_apps", + ":test_support", "//base", "//base/test:test_support", "//chrome/app/theme:chrome_unscaled_resources_grit",
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc index d76d588a..fbadf69 100644 --- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc +++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/values.h" #include "chrome/browser/prefs/browser_prefs.h" +#include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h" #include "chrome/browser/web_applications/components/pending_app_manager.h" #include "chrome/browser/web_applications/components/test_pending_app_manager.h" #include "chrome/browser/web_applications/components/web_app_constants.h" @@ -84,14 +85,26 @@ class WebAppPolicyManagerTest : public ChromeRenderViewHostTestHarness { public: - WebAppPolicyManagerTest() = default; + WebAppPolicyManagerTest() + : test_web_app_provider_creator_( + base::BindOnce(&WebAppPolicyManagerTest::CreateWebAppProvider, + base::Unretained(this))) {} + ~WebAppPolicyManagerTest() override = default; - void SetUp() override { - ChromeRenderViewHostTestHarness::SetUp(); - // Reset WebAppProvider so that its WebAppPolicyManager doesn't interfere - // with tests. - web_app::WebAppProvider::Get(profile())->Reset(); + std::unique_ptr<KeyedService> CreateWebAppProvider(Profile* profile) { + auto provider = std::make_unique<TestWebAppProvider>(profile); + + auto test_pending_app_manager = std::make_unique<TestPendingAppManager>(); + test_pending_app_manager_ = test_pending_app_manager.get(); + provider->SetPendingAppManager(std::move(test_pending_app_manager)); + + auto web_app_policy_manager = std::make_unique<WebAppPolicyManager>( + profile, test_pending_app_manager_); + web_app_policy_manager_ = web_app_policy_manager.get(); + provider->SetWebAppPolicyManager(std::move(web_app_policy_manager)); + + return provider; } std::string GenerateFakeExtensionId(GURL& url) { @@ -99,7 +112,6 @@ } void SimulatePreviouslyInstalledApp( - TestPendingAppManager* pending_app_manager, GURL url, InstallSource install_source) { std::string id = GenerateFakeExtensionId(url); @@ -109,21 +121,30 @@ ExtensionIdsMap extension_ids_map(profile()->GetPrefs()); extension_ids_map.Insert(url, id, install_source); - pending_app_manager->SimulatePreviouslyInstalledApp(url, install_source); + pending_app_manager()->SimulatePreviouslyInstalledApp(url, install_source); } + protected: + TestPendingAppManager* pending_app_manager() { + return test_pending_app_manager_; + } + + WebAppPolicyManager* policy_manager() { return web_app_policy_manager_; } + private: + TestWebAppProviderCreator test_web_app_provider_creator_; + TestPendingAppManager* test_pending_app_manager_ = nullptr; + WebAppPolicyManager* web_app_policy_manager_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(WebAppPolicyManagerTest); }; TEST_F(WebAppPolicyManagerTest, NoForceInstalledAppsPrefValue) { - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); + base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); EXPECT_TRUE(apps_to_install.empty()); } @@ -131,13 +152,10 @@ profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, base::Value(base::Value::Type::LIST)); - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); EXPECT_TRUE(apps_to_install.empty()); } @@ -148,13 +166,10 @@ list.GetList().push_back(GetTabbedItem()); profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list)); - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); std::vector<PendingAppManager::AppInfo> expected_apps_to_install; expected_apps_to_install.push_back(GetWindowedAppInfo()); @@ -168,13 +183,10 @@ list.GetList().push_back(GetDefaultContainerItem()); profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(list)); - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); std::vector<PendingAppManager::AppInfo> expected_apps_to_install; expected_apps_to_install.push_back(GetDefaultContainerAppInfo()); @@ -188,13 +200,10 @@ profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(first_list)); - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); std::vector<PendingAppManager::AppInfo> expected_apps_to_install; expected_apps_to_install.push_back(GetWindowedAppInfo()); @@ -214,16 +223,13 @@ } TEST_F(WebAppPolicyManagerTest, UninstallAppInstalledInPreviousSession) { - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - // Simulate two policy apps and a regular app that were installed in the // previous session. - SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kWindowedUrl), + SimulatePreviouslyInstalledApp(GURL(kWindowedUrl), InstallSource::kExternalPolicy); - SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kTabbedUrl), + SimulatePreviouslyInstalledApp(GURL(kTabbedUrl), InstallSource::kExternalPolicy); - SimulatePreviouslyInstalledApp(pending_app_manager.get(), - GURL(kDefaultContainerUrl), + SimulatePreviouslyInstalledApp(GURL(kDefaultContainerUrl), InstallSource::kInternal); // Push a policy with only one of the apps. @@ -232,28 +238,24 @@ profile()->GetPrefs()->Set(prefs::kWebAppInstallForceList, std::move(first_list)); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); // We should only try to install the app in the policy. std::vector<PendingAppManager::AppInfo> expected_apps_to_install; expected_apps_to_install.push_back(GetWindowedAppInfo()); - EXPECT_EQ(pending_app_manager->install_requests(), expected_apps_to_install); + EXPECT_EQ(pending_app_manager()->install_requests(), + expected_apps_to_install); // We should try to uninstall the app that is no longer in the policy. EXPECT_EQ(std::vector<GURL>({GURL(kTabbedUrl)}), - pending_app_manager->uninstall_requests()); + pending_app_manager()->uninstall_requests()); } // Tests that we correctly uninstall an app that we installed in the same // session. TEST_F(WebAppPolicyManagerTest, UninstallAppInstalledInCurrentSession) { - auto pending_app_manager = std::make_unique<TestPendingAppManager>(); - WebAppPolicyManager web_app_policy_manager(profile(), - pending_app_manager.get()); - web_app_policy_manager.Start(); + policy_manager()->Start(); base::RunLoop().RunUntilIdle(); // Add two sites, one that opens in a window and one that opens in a tab. @@ -264,7 +266,7 @@ std::move(first_list)); base::RunLoop().RunUntilIdle(); - const auto& apps_to_install = pending_app_manager->install_requests(); + const auto& apps_to_install = pending_app_manager()->install_requests(); std::vector<PendingAppManager::AppInfo> expected_apps_to_install; expected_apps_to_install.push_back(GetWindowedAppInfo()); @@ -286,7 +288,7 @@ EXPECT_EQ(apps_to_install, expected_apps_to_install); EXPECT_EQ(std::vector<GURL>({GURL(kTabbedUrl)}), - pending_app_manager->uninstall_requests()); + pending_app_manager()->uninstall_requests()); } } // namespace web_app
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc index b5615984..c8fbfc9 100644 --- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc +++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h" #include "chrome/browser/web_applications/test/test_system_web_app_manager.h" -#include "chrome/browser/web_applications/web_app_provider_factory.h" #include "chrome/common/chrome_features.h" #include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h" #include "chrome/grit/browser_resources.h" @@ -113,7 +112,10 @@ }; SystemWebAppManagerBrowserTest::SystemWebAppManagerBrowserTest() - : factory_(std::make_unique<TestWebUIControllerFactory>()) { + : factory_(std::make_unique<TestWebUIControllerFactory>()), + test_web_app_provider_creator_( + base::BindOnce(&SystemWebAppManagerBrowserTest::CreateWebAppProvider, + base::Unretained(this))) { scoped_feature_list_.InitWithFeatures( {features::kDesktopPWAWindowing, features::kSystemWebApps}, {}); content::WebUIControllerFactory::RegisterFactory(factory_.get()); @@ -123,36 +125,15 @@ content::WebUIControllerFactory::UnregisterFactoryForTesting(factory_.get()); } -void SystemWebAppManagerBrowserTest::SetUpInProcessBrowserTestFixture() { - will_create_browser_context_services_subscription_ = - BrowserContextDependencyManager::GetInstance() - ->RegisterWillCreateBrowserContextServicesCallbackForTesting( - base::BindRepeating(&SystemWebAppManagerBrowserTest:: - OnWillCreateBrowserContextServices, - base::Unretained(this))); -} - -void SystemWebAppManagerBrowserTest::OnWillCreateBrowserContextServices( - content::BrowserContext* context) { - WebAppProviderFactory::GetInstance()->SetTestingFactory( - context, - base::BindRepeating(&SystemWebAppManagerBrowserTest::CreateWebAppProvider, - base::Unretained(this))); -} - std::unique_ptr<KeyedService> -SystemWebAppManagerBrowserTest::CreateWebAppProvider( - content::BrowserContext* context) { - Profile* profile = Profile::FromBrowserContext(context); - - if (!SystemWebAppManager::IsEnabled()) - return nullptr; +SystemWebAppManagerBrowserTest::CreateWebAppProvider(Profile* profile) { + DCHECK(SystemWebAppManager::IsEnabled()); auto provider = std::make_unique<TestWebAppProvider>(profile); // Create all real subsystems but do not start them: provider->Init(); - // But override SystemWebAppManager with TestSystemWebAppManager: + // Override SystemWebAppManager with TestSystemWebAppManager: DCHECK(!test_system_web_app_manager_); auto test_system_web_app_manager = std::make_unique<TestSystemWebAppManager>( profile, &provider->pending_app_manager());
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.h b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.h index 83e2009..0752960 100644 --- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.h +++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.h
@@ -7,18 +7,14 @@ #include <memory> -#include "base/callback_list.h" #include "base/macros.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h" #include "chrome/test/base/in_process_browser_test.h" class Browser; class KeyedService; -namespace content { -class BrowserContext; -} - namespace web_app { class TestSystemWebAppManager; @@ -29,25 +25,17 @@ SystemWebAppManagerBrowserTest(); ~SystemWebAppManagerBrowserTest() override; - // InProcessBrowserTest: - void SetUpInProcessBrowserTestFixture() override; - protected: Browser* WaitForSystemAppInstallAndLaunch(); private: - void OnWillCreateBrowserContextServices(content::BrowserContext* context); - std::unique_ptr<KeyedService> CreateWebAppProvider( - content::BrowserContext* context); + std::unique_ptr<KeyedService> CreateWebAppProvider(Profile* profile); base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<TestWebUIControllerFactory> factory_; + TestWebAppProviderCreator test_web_app_provider_creator_; TestSystemWebAppManager* test_system_web_app_manager_ = nullptr; - std::unique_ptr< - base::CallbackList<void(content::BrowserContext*)>::Subscription> - will_create_browser_context_services_subscription_; - DISALLOW_COPY_AND_ASSIGN(SystemWebAppManagerBrowserTest); };
diff --git a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc index fb59e9f36..9b114b56a 100644 --- a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc +++ b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc
@@ -4,7 +4,16 @@ #include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h" +#include <utility> + +#include "base/bind.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/web_applications/components/pending_app_manager.h" +#include "chrome/browser/web_applications/policy/web_app_policy_manager.h" #include "chrome/browser/web_applications/system_web_app_manager.h" +#include "chrome/browser/web_applications/web_app_provider_factory.h" +#include "chrome/browser/web_applications/web_app_utils.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" namespace web_app { @@ -13,9 +22,48 @@ TestWebAppProvider::~TestWebAppProvider() = default; +void TestWebAppProvider::SetPendingAppManager( + std::unique_ptr<PendingAppManager> pending_app_manager) { + pending_app_manager_ = std::move(pending_app_manager); +} + void TestWebAppProvider::SetSystemWebAppManager( std::unique_ptr<SystemWebAppManager> system_web_app_manager) { system_web_app_manager_ = std::move(system_web_app_manager); } +void TestWebAppProvider::SetWebAppPolicyManager( + std::unique_ptr<WebAppPolicyManager> web_app_policy_manager) { + web_app_policy_manager_ = std::move(web_app_policy_manager); +} + +TestWebAppProviderCreator::TestWebAppProviderCreator( + CreateWebAppProviderCallback callback) + : callback_(std::move(callback)) { + will_create_browser_context_services_subscription_ = + BrowserContextDependencyManager::GetInstance() + ->RegisterWillCreateBrowserContextServicesCallbackForTesting( + base::BindRepeating(&TestWebAppProviderCreator:: + OnWillCreateBrowserContextServices, + base::Unretained(this))); +} + +TestWebAppProviderCreator::~TestWebAppProviderCreator() = default; + +void TestWebAppProviderCreator::OnWillCreateBrowserContextServices( + content::BrowserContext* context) { + WebAppProviderFactory::GetInstance()->SetTestingFactory( + context, + base::BindRepeating(&TestWebAppProviderCreator::CreateWebAppProvider, + base::Unretained(this))); +} + +std::unique_ptr<KeyedService> TestWebAppProviderCreator::CreateWebAppProvider( + content::BrowserContext* context) { + Profile* profile = Profile::FromBrowserContext(context); + if (!AreWebAppsEnabled(profile) || !callback_) + return nullptr; + return std::move(callback_).Run(profile); +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h index dc7ade8..fc12f2a 100644 --- a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h +++ b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h
@@ -7,21 +7,53 @@ #include <memory> +#include "base/callback.h" +#include "base/callback_list.h" #include "chrome/browser/web_applications/web_app_provider.h" class Profile; +namespace content { +class BrowserContext; +} + namespace web_app { +class PendingAppManager; class SystemWebAppManager; +class WebAppPolicyManager; class TestWebAppProvider : public WebAppProvider { public: explicit TestWebAppProvider(Profile* profile); ~TestWebAppProvider() override; + void SetPendingAppManager( + std::unique_ptr<PendingAppManager> pending_app_manager); void SetSystemWebAppManager( std::unique_ptr<SystemWebAppManager> system_web_app_manager); + void SetWebAppPolicyManager( + std::unique_ptr<WebAppPolicyManager> web_app_policy_manager); +}; + +class TestWebAppProviderCreator { + public: + using CreateWebAppProviderCallback = + base::OnceCallback<std::unique_ptr<KeyedService>(Profile* profile)>; + + explicit TestWebAppProviderCreator(CreateWebAppProviderCallback callback); + ~TestWebAppProviderCreator(); + + private: + void OnWillCreateBrowserContextServices(content::BrowserContext* context); + std::unique_ptr<KeyedService> CreateWebAppProvider( + content::BrowserContext* context); + + CreateWebAppProviderCallback callback_; + + std::unique_ptr< + base::CallbackList<void(content::BrowserContext*)>::Subscription> + will_create_browser_context_services_subscription_; }; } // namespace web_app
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom index 18cb077..ba90a38d0 100644 --- a/chrome/services/app_service/public/mojom/types.mojom +++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -80,6 +80,7 @@ enum IconType { kUnknown, kArc, + kCrostini, kExtension, kResource, };
diff --git a/chrome/services/cups_ipp_parser/BUILD.gn b/chrome/services/cups_ipp_parser/BUILD.gn index 0628ec0..ed87417 100644 --- a/chrome/services/cups_ipp_parser/BUILD.gn +++ b/chrome/services/cups_ipp_parser/BUILD.gn
@@ -22,6 +22,7 @@ ] public_deps = [ + "//chrome/services/cups_ipp_parser/public/mojom", "//mojo/public/mojom/base", "//printing", "//services/service_manager/public/cpp", @@ -31,10 +32,7 @@ if (enable_service) { configs += [ "//printing:cups" ] sources += [ "ipp_parser.cc" ] - deps += [ - "//chrome/services/cups_ipp_parser/public/cpp", - "//chrome/services/cups_ipp_parser/public/mojom", - ] + deps += [ "//chrome/services/cups_ipp_parser/public/cpp" ] } else { sources += [ "fake_ipp_parser.cc" ] }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 309436d..69babd6 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2237,6 +2237,25 @@ } } +if (is_linux || is_mac || is_win) { + group("browser_tests_apprtc") { + testonly = true + data_deps = [ + ":browser_tests", + ] + data = [ + "//out/apprtc/out/app_engine/", + "//out/apprtc/temp/google-cloud-sdk/", + "//out/collider/", + ] + if (is_win) { + write_runtime_deps = "$root_out_dir/$target_name.exe.runtime_deps" + } else { + write_runtime_deps = "$root_out_dir/$target_name.runtime_deps" + } + } +} + group("telemetry_perf_unittests") { testonly = true deps = [
diff --git a/chromeos/services/assistant/platform/system_provider_impl.h b/chromeos/services/assistant/platform/system_provider_impl.h index 55e922ba..e5a9dfd4 100644 --- a/chromeos/services/assistant/platform/system_provider_impl.h +++ b/chromeos/services/assistant/platform/system_provider_impl.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "libassistant/shared/public/platform_system.h" #include "services/device/public/mojom/battery_monitor.mojom.h" +#include "services/device/public/mojom/battery_status.mojom.h" namespace chromeos { namespace assistant {
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc index 7df8934..fbc6997d9 100644 --- a/components/exo/client_controlled_shell_surface.cc +++ b/components/exo/client_controlled_shell_surface.cc
@@ -904,7 +904,8 @@ case ash::mojom::WindowStateType::MAXIMIZED: case ash::mojom::WindowStateType::FULLSCREEN: - animation_type = ash::wm::ClientControlledState::kAnimationCrossFade; + if (!window_state->IsPip()) + animation_type = ash::wm::ClientControlledState::kAnimationCrossFade; break; default:
diff --git a/components/gwp_asan/common/BUILD.gn b/components/gwp_asan/common/BUILD.gn index b8d77a5..dc451bb5 100644 --- a/components/gwp_asan/common/BUILD.gn +++ b/components/gwp_asan/common/BUILD.gn
@@ -2,11 +2,15 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//testing/libfuzzer/fuzzer_test.gni") + static_library("common") { sources = [ "allocator_state.cc", "allocator_state.h", "crash_key_name.h", + "pack_stack_trace.cc", + "pack_stack_trace.h", ] deps = [ @@ -18,6 +22,7 @@ testonly = true sources = [ "allocator_state_unittest.cc", + "pack_stack_trace_unittest.cc", ] deps = [ ":common", @@ -25,3 +30,21 @@ "//testing/gtest", ] } + +fuzzer_test("pack_stack_trace_unpack_fuzzer") { + sources = [ + "pack_stack_trace_unpack_fuzzer.cc", + ] + deps = [ + "//components/gwp_asan/common", + ] +} + +fuzzer_test("pack_stack_trace_differential_fuzzer") { + sources = [ + "pack_stack_trace_differential_fuzzer.cc", + ] + deps = [ + "//components/gwp_asan/common", + ] +}
diff --git a/components/gwp_asan/common/pack_stack_trace.cc b/components/gwp_asan/common/pack_stack_trace.cc new file mode 100644 index 0000000..7ddd27b --- /dev/null +++ b/components/gwp_asan/common/pack_stack_trace.cc
@@ -0,0 +1,110 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gwp_asan/common/pack_stack_trace.h" + +namespace gwp_asan { +namespace internal { + +namespace { + +// Encode variable-length integer to |out|, returns 0 if there was not enough +// space to finish writing it or the length of the encoded integer otherwise. +size_t VarIntEncode(uintptr_t value, uint8_t* out, size_t out_len) { + for (size_t i = 0; i < out_len; i++) { + out[i] = value & 0x7f; + value >>= 7; + if (!value) + return i + 1; + + out[i] |= 0x80; + } + + return 0; +} + +// Decode a variable-length integer to |out|, returns 0 if reading it failed or +// the length of the decoded integer otherwise. +size_t VarIntDecode(const uint8_t* in, size_t in_len, uintptr_t* out) { + uintptr_t result = 0; + size_t shift = 0; + for (size_t i = 0; i < in_len; i++) { + result |= static_cast<uintptr_t>(in[i] & 0x7f) << shift; + if (in[i] < 0x80) { + *out = result; + return i + 1; + } + + shift += 7; + // Disallow overflowing the range of the output integer. + if (shift >= sizeof(uintptr_t) * 8) + return 0; + } + + return 0; +} + +uintptr_t ZigzagEncode(uintptr_t value) { + uintptr_t encoded = value << 1; + if (static_cast<intptr_t>(value) >= 0) + return encoded; + return ~encoded; +} + +uintptr_t ZigzagDecode(uintptr_t value) { + uintptr_t decoded = value >> 1; + if (!(value & 1)) + return decoded; + return ~decoded; +} + +} // namespace + +size_t Pack(const uintptr_t* unpacked, + size_t unpacked_size, + uint8_t* packed, + size_t packed_max_size) { + size_t idx = 0; + for (size_t cur_depth = 0; cur_depth < unpacked_size; cur_depth++) { + uintptr_t diff = unpacked[cur_depth]; + if (cur_depth > 0) + diff -= unpacked[cur_depth - 1]; + size_t encoded_len = + VarIntEncode(ZigzagEncode(diff), packed + idx, packed_max_size - idx); + if (!encoded_len) + break; + + idx += encoded_len; + } + + return idx; +} + +size_t Unpack(const uint8_t* packed, + size_t packed_size, + uintptr_t* unpacked, + size_t unpacked_max_size) { + size_t cur_depth; + size_t idx = 0; + for (cur_depth = 0; cur_depth < unpacked_max_size; cur_depth++) { + uintptr_t encoded_diff; + size_t decoded_len = + VarIntDecode(packed + idx, packed_size - idx, &encoded_diff); + if (!decoded_len) + break; + idx += decoded_len; + + unpacked[cur_depth] = ZigzagDecode(encoded_diff); + if (cur_depth > 0) + unpacked[cur_depth] += unpacked[cur_depth - 1]; + } + + if (idx != packed_size && cur_depth != unpacked_max_size) + return 0; + + return cur_depth; +} + +} // namespace internal +} // namespace gwp_asan
diff --git a/components/gwp_asan/common/pack_stack_trace.h b/components/gwp_asan/common/pack_stack_trace.h new file mode 100644 index 0000000..cae0584 --- /dev/null +++ b/components/gwp_asan/common/pack_stack_trace.h
@@ -0,0 +1,40 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_GWP_ASAN_COMMON_PACK_STACK_TRACE_H_ +#define COMPONENTS_GWP_ASAN_COMMON_PACK_STACK_TRACE_H_ + +#include <stddef.h> +#include <stdint.h> + +// These routines 'compress' a stack trace by storing a stack trace as a +// starting address, followed by offsets from the previous pointer. All values +// are stored using variable-length integers to reduce space. Furthermore, they +// are zigzag encoded, like in protobuf encoding, to store negative offsets +// efficiently. On 64-bit platforms this packing can reduce space required to +// store a stack trace by over 50%. + +namespace gwp_asan { +namespace internal { + +// From the stack trace in |unpacked| of length |unpacked_size|, pack it into +// the buffer |packed| with maximum length |packed_max_size|. The return value +// is the number of bytes that were written to the output buffer. +size_t Pack(const uintptr_t* unpacked, + size_t unpacked_size, + uint8_t* packed, + size_t packed_max_size); + +// From the packed stack trace in |packed| of length |packed_size|, write the +// unpacked stack trace of maximum length |unpacked_max_size| into |unpacked|. +// Returns the number of entries un packed, or 0 on error. +size_t Unpack(const uint8_t* packed, + size_t packed_size, + uintptr_t* unpacked, + size_t unpacked_max_size); + +} // namespace internal +} // namespace gwp_asan + +#endif // COMPONENTS_GWP_ASAN_COMMON_PACK_STACK_TRACE_H_
diff --git a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc new file mode 100644 index 0000000..f964385 --- /dev/null +++ b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
@@ -0,0 +1,45 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gwp_asan/common/pack_stack_trace.h" + +#include <string.h> +#include <algorithm> + +// Tests that whatever we give to Pack() is the same as what comes out of +// Unpack(). + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + if (Size < sizeof(size_t) * 2) + return 0; + + size_t unpacked_max_size = reinterpret_cast<const size_t*>(Data)[0]; + size_t packed_max_size = reinterpret_cast<const size_t*>(Data)[1]; + Data += sizeof(size_t) * 2; + Size -= sizeof(size_t) * 2; + + size_t entries = Size / sizeof(uintptr_t); + + // We don't need a buffer large than Size*10 as the longest variable length + // encoding of a 64-bit integer is 10 bytes long.) + size_t array_size = std::min(Size * 10, packed_max_size); + uint8_t packed[array_size]; + size_t packed_size = + gwp_asan::internal::Pack(reinterpret_cast<const uintptr_t*>(Data), + entries, packed, packed_max_size); + if (packed_size > sizeof(packed)) + __builtin_trap(); + + uintptr_t unpacked[std::min(unpacked_max_size, Size)]; + size_t unpacked_size = gwp_asan::internal::Unpack( + packed, packed_size, unpacked, unpacked_max_size); + // We can only be sure there was enough room to pack the entire input when + // packed_max_size was larger than Size*10. + if (packed_max_size > array_size && + unpacked_size != std::min(entries, unpacked_max_size)) + __builtin_trap(); + if (memcmp(Data, unpacked, unpacked_size * sizeof(uintptr_t))) + __builtin_trap(); + return 0; +}
diff --git a/components/gwp_asan/common/pack_stack_trace_unittest.cc b/components/gwp_asan/common/pack_stack_trace_unittest.cc new file mode 100644 index 0000000..98dbfdc --- /dev/null +++ b/components/gwp_asan/common/pack_stack_trace_unittest.cc
@@ -0,0 +1,98 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gwp_asan/common/pack_stack_trace.h" + +#include "base/test/gtest_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gwp_asan { +namespace internal { + +TEST(PackTest, TrivialExample) { + constexpr size_t kTestEntries = 3; + + uintptr_t test[kTestEntries] = {1, 2, 3}; + uint8_t packed[8]; + uintptr_t unpacked[kTestEntries]; + + const size_t packed_size = Pack(test, kTestEntries, packed, sizeof(packed)); + ASSERT_EQ(packed_size, 3U); + // ZigzagEncode(1) == 2 + EXPECT_EQ(packed[0], 2U); + EXPECT_EQ(packed[1], 2U); + EXPECT_EQ(packed[2], 2U); + EXPECT_EQ(Unpack(packed, packed_size, unpacked, kTestEntries), kTestEntries); + EXPECT_EQ(unpacked[0], 1U); + EXPECT_EQ(unpacked[1], 2U); + EXPECT_EQ(unpacked[2], 3U); +} + +TEST(PackTest, DecreasingSequence) { + constexpr size_t kTestEntries = 3; + + uintptr_t test[kTestEntries] = {3, 2, 1}; + uint8_t packed[8]; + uintptr_t unpacked[kTestEntries]; + + const size_t packed_size = Pack(test, kTestEntries, packed, sizeof(packed)); + ASSERT_EQ(packed_size, 3U); + // ZigzagEncode(3) == 6 + // ZigzagEncode(-1) == 1 + EXPECT_EQ(packed[0], 6U); + EXPECT_EQ(packed[1], 1U); + EXPECT_EQ(packed[2], 1U); + EXPECT_EQ(Unpack(packed, packed_size, unpacked, kTestEntries), kTestEntries); + EXPECT_EQ(unpacked[0], 3U); + EXPECT_EQ(unpacked[1], 2U); + EXPECT_EQ(unpacked[2], 1U); +} + +TEST(PackTest, MultibyteVarInts) { + constexpr size_t kTestEntries = 1; + + uintptr_t test[kTestEntries] = {0x40}; + uint8_t packed[8]; + uintptr_t unpacked[kTestEntries]; + + const size_t packed_size = Pack(test, kTestEntries, packed, sizeof(packed)); + ASSERT_EQ(packed_size, 2U); + // ZigzagEncode(0x40) == 0x80 + EXPECT_EQ(packed[0], 0x80U); + EXPECT_EQ(packed[1], 0x01U); + EXPECT_EQ(Unpack(packed, packed_size, unpacked, kTestEntries), kTestEntries); + EXPECT_EQ(unpacked[0], 0x40U); +} + +TEST(PackTest, UnpackFailsOnOutOfBoundsVarInt) { + uint8_t packed[11] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x00}; + uintptr_t unpacked[1]; + + EXPECT_EQ(Unpack(packed, 11, unpacked, 1), 0U); +} + +TEST(PackTest, UnpackFailsOnBufferTooSmall) { + uint8_t packed[2] = {0x80, 0x00}; + uintptr_t unpacked[2]; + + // Fail + EXPECT_EQ(Unpack(packed, 1, unpacked, 1), 0U); + // Success + EXPECT_EQ(Unpack(packed, 2, unpacked, 1), 1U); + EXPECT_EQ(Unpack(packed, 2, unpacked, 2), 1U); +} + +TEST(PackTest, PackFailsOnBufferTooSmall) { + uintptr_t test[] = {0x40, 0x41}; + uint8_t packed[4]; + + EXPECT_EQ(Pack(test, 2, packed, 1), 0U); + EXPECT_EQ(Pack(test, 2, packed, 2), 2U); + EXPECT_EQ(Pack(test, 2, packed, 3), 3U); + EXPECT_EQ(Pack(test, 2, packed, 4), 3U); +} + +} // namespace internal +} // namespace gwp_asan
diff --git a/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc new file mode 100644 index 0000000..c7f807f --- /dev/null +++ b/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc
@@ -0,0 +1,25 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/gwp_asan/common/pack_stack_trace.h" + +#include <algorithm> + +// Tests that Unpack() correctly handles arbitrary inputs. + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { + // The first sizeof(size_t) bytes of the input are treated as the + // unpacked_max_size argument to Unpack. + if (Size < sizeof(size_t)) + return 0; + + size_t unpacked_max_size = *reinterpret_cast<const size_t*>(Data); + Data += sizeof(size_t); + Size -= sizeof(size_t); + + // This should always be large enough to hold the output. + uintptr_t unpacked[std::min(Size, unpacked_max_size)]; + gwp_asan::internal::Unpack(Data, Size, unpacked, unpacked_max_size); + return 0; +}
diff --git a/components/heap_profiling/heap_profiling_test_shim.cc b/components/heap_profiling/heap_profiling_test_shim.cc index bae0523..ca6efeb9 100644 --- a/components/heap_profiling/heap_profiling_test_shim.cc +++ b/components/heap_profiling/heap_profiling_test_shim.cc
@@ -19,7 +19,8 @@ } HeapProfilingTestShim::HeapProfilingTestShim(JNIEnv* env, jobject obj) {} -HeapProfilingTestShim::~HeapProfilingTestShim() = default; + +HeapProfilingTestShim::~HeapProfilingTestShim() {} void HeapProfilingTestShim::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { @@ -32,7 +33,6 @@ const base::android::JavaParamRef<jstring>& mode, jboolean dynamically_start_profiling, const base::android::JavaParamRef<jstring>& stack_mode, - jboolean stream_samples, jboolean should_sample, jboolean sample_everything) { heap_profiling::TestDriver driver; @@ -42,7 +42,6 @@ options.stack_mode = heap_profiling::ConvertStringToStackMode( base::android::ConvertJavaStringToUTF8(stack_mode)); options.profiling_already_started = !dynamically_start_profiling; - options.stream_samples = stream_samples; options.should_sample = should_sample; options.sample_everything = sample_everything; return driver.RunTest(options);
diff --git a/components/heap_profiling/heap_profiling_test_shim.h b/components/heap_profiling/heap_profiling_test_shim.h index 558933c..134d548 100644 --- a/components/heap_profiling/heap_profiling_test_shim.h +++ b/components/heap_profiling/heap_profiling_test_shim.h
@@ -23,7 +23,6 @@ const base::android::JavaParamRef<jstring>& mode, jboolean dynamically_start_profiling, const base::android::JavaParamRef<jstring>& stack_mode, - jboolean stream_samples, jboolean should_sample, jboolean sample_everything);
diff --git a/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java b/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java index c7f788e..f55ceaf8 100644 --- a/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java +++ b/components/heap_profiling/javatests/src/org/chromium/components/heap_profiling/HeapProfilingTestShim.java
@@ -23,9 +23,9 @@ * rather than native stacks. */ public boolean runTestForMode(String mode, boolean dynamicallyStartProfiling, String stackMode, - boolean streamSamples, boolean shouldSample, boolean sampleEverything) { + boolean shouldSample, boolean sampleEverything) { return nativeRunTestForMode(mNativeHeapProfilingTestShim, mode, dynamicallyStartProfiling, - stackMode, streamSamples, shouldSample, sampleEverything); + stackMode, shouldSample, sampleEverything); } /** @@ -43,6 +43,6 @@ private native long nativeInit(); private native void nativeDestroy(long nativeHeapProfilingTestShim); private native boolean nativeRunTestForMode(long nativeHeapProfilingTestShim, String mode, - boolean dynamicallyStartProfiling, String stackMode, boolean streamSamples, - boolean shouldSample, boolean sampleEverything); + boolean dynamicallyStartProfiling, String stackMode, boolean shouldSample, + boolean sampleEverything); }
diff --git a/components/heap_profiling/supervisor.cc b/components/heap_profiling/supervisor.cc index 987902b3..70afb5f 100644 --- a/components/heap_profiling/supervisor.cc +++ b/components/heap_profiling/supervisor.cc
@@ -60,27 +60,24 @@ void Supervisor::Start(content::ServiceManagerConnection* connection, base::OnceClosure closure) { - // TODO(alph): Obtain stream_samples from the command line / Finch. Start(connection, GetModeForStartup(), GetStackModeForStartup(), - true /* stream_samples */, GetSamplingRateForStartup(), - std::move(closure)); + GetSamplingRateForStartup(), std::move(closure)); } void Supervisor::Start(content::ServiceManagerConnection* connection, Mode mode, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate, base::OnceClosure closure) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(!started_); base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}) - ->PostTask(FROM_HERE, base::BindOnce(&Supervisor::StartServiceOnIOThread, - base::Unretained(this), - connection->GetConnector()->Clone(), - mode, stack_mode, stream_samples, - sampling_rate, std::move(closure))); + ->PostTask(FROM_HERE, + base::BindOnce(&Supervisor::StartServiceOnIOThread, + base::Unretained(this), + connection->GetConnector()->Clone(), mode, + stack_mode, sampling_rate, std::move(closure))); } Mode Supervisor::GetMode() { @@ -183,13 +180,12 @@ std::unique_ptr<service_manager::Connector> connector, Mode mode, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate, base::OnceClosure closure) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - controller_.reset(new Controller(std::move(connector), stack_mode, - stream_samples, sampling_rate)); + controller_.reset( + new Controller(std::move(connector), stack_mode, sampling_rate)); base::WeakPtr<Controller> controller_weak_ptr = controller_->GetWeakPtr(); base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
diff --git a/components/heap_profiling/supervisor.h b/components/heap_profiling/supervisor.h index 592b72e..b4b8515 100644 --- a/components/heap_profiling/supervisor.h +++ b/components/heap_profiling/supervisor.h
@@ -72,7 +72,6 @@ void Start(content::ServiceManagerConnection* connection, Mode mode, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate, base::OnceClosure callback); @@ -117,7 +116,6 @@ std::unique_ptr<service_manager::Connector> connector, Mode mode, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate, base::OnceClosure callback);
diff --git a/components/heap_profiling/test_driver.cc b/components/heap_profiling/test_driver.cc index 6799497c..7a6decc 100644 --- a/components/heap_profiling/test_driver.cc +++ b/components/heap_profiling/test_driver.cc
@@ -559,7 +559,7 @@ base::WaitableEvent::InitialState::NOT_SIGNALED) { partition_allocator_.init(); } -TestDriver::~TestDriver() = default; +TestDriver::~TestDriver() {} bool TestDriver::RunTest(const Options& options) { options_ = options; @@ -723,8 +723,8 @@ ? (options_.sample_everything ? 2 : kSampleRate) : 1; Supervisor::GetInstance()->Start(connection, options_.mode, - options_.stack_mode, options_.stream_samples, - sampling_rate, std::move(start_callback)); + options_.stack_mode, sampling_rate, + std::move(start_callback)); return true; } @@ -776,8 +776,8 @@ ? (options_.sample_everything ? 2 : kSampleRate) : 1; Supervisor::GetInstance()->Start(connection, options_.mode, - options_.stack_mode, options_.stream_samples, - sampling_rate, std::move(start_callback)); + options_.stack_mode, sampling_rate, + std::move(start_callback)); run_loop->Run();
diff --git a/components/heap_profiling/test_driver.h b/components/heap_profiling/test_driver.h index 03e447b..f897c672 100644 --- a/components/heap_profiling/test_driver.h +++ b/components/heap_profiling/test_driver.h
@@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "base/synchronization/waitable_event.h" -#include "components/services/heap_profiling/public/cpp/settings.h" #include "components/services/heap_profiling/public/mojom/heap_profiling_client.mojom.h" namespace base { @@ -20,6 +19,8 @@ namespace heap_profiling { +enum class Mode; + // This class runs tests for the Heap Profiling Service, a cross-platform, // multi-process component. // @@ -44,28 +45,23 @@ public: struct Options { // The profiling mode to test. - Mode mode = Mode::kBrowser; + Mode mode; // The stack profiling mode to test. - mojom::StackMode stack_mode = mojom::StackMode::NATIVE_WITHOUT_THREAD_NAMES; - - // Whether the client should stream samples as they are collected through - // the provided pipe. When false the samples are accumulated on the client - // side and can be retrieved later. - bool stream_samples = true; + mojom::StackMode stack_mode; // Whether the caller has already started profiling with the given mode. // When false, the test driver is responsible for starting profiling. - bool profiling_already_started = false; + bool profiling_already_started; // Whether to test sampling. - bool should_sample = true; + bool should_sample; // When set to true, the internal sampling_rate is set to 2. While this // doesn't record all allocations, it should record all test allocations // made in this file with exponentially high probability. // When set to false, the internal sampling rate is set to 10000. - bool sample_everything = false; + bool sample_everything; }; TestDriver();
diff --git a/components/language/content/browser/geo_language_provider.cc b/components/language/content/browser/geo_language_provider.cc index d924c95..78b17386 100644 --- a/components/language/content/browser/geo_language_provider.cc +++ b/components/language/content/browser/geo_language_provider.cc
@@ -14,6 +14,7 @@ #include "components/prefs/pref_service.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/device/public/mojom/constants.mojom.h" +#include "services/device/public/mojom/geoposition.mojom.h" #include "services/device/public/mojom/public_ip_address_geolocation_provider.mojom.h" #include "services/service_manager/public/cpp/connector.h"
diff --git a/components/language/content/browser/test_utils.h b/components/language/content/browser/test_utils.h index a0c9593..74310cab 100644 --- a/components/language/content/browser/test_utils.h +++ b/components/language/content/browser/test_utils.h
@@ -8,6 +8,7 @@ #include "mojo/public/cpp/bindings/binding.h" #include "services/device/public/mojom/constants.mojom.h" #include "services/device/public/mojom/geolocation.mojom.h" +#include "services/device/public/mojom/geoposition.mojom.h" #include "services/device/public/mojom/public_ip_address_geolocation_provider.mojom.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/mojom/connector.mojom.h"
diff --git a/components/mirroring/service/captured_audio_input.cc b/components/mirroring/service/captured_audio_input.cc index 3f1d02f..c863499 100644 --- a/components/mirroring/service/captured_audio_input.cc +++ b/components/mirroring/service/captured_audio_input.cc
@@ -5,6 +5,7 @@ #include "components/mirroring/service/captured_audio_input.h" #include "base/logging.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" namespace mirroring {
diff --git a/components/mirroring/service/captured_audio_input_unittest.cc b/components/mirroring/service/captured_audio_input_unittest.cc index 588311f..59541ba 100644 --- a/components/mirroring/service/captured_audio_input_unittest.cc +++ b/components/mirroring/service/captured_audio_input_unittest.cc
@@ -9,6 +9,7 @@ #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" #include "media/base/audio_parameters.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/buffer.h" #include "mojo/public/cpp/system/platform_handle.h"
diff --git a/components/mirroring/service/fake_video_capture_host.cc b/components/mirroring/service/fake_video_capture_host.cc index 201edc7..afb423d 100644 --- a/components/mirroring/service/fake_video_capture_host.cc +++ b/components/mirroring/service/fake_video_capture_host.cc
@@ -6,6 +6,7 @@ #include "base/memory/read_only_shared_memory_region.h" #include "media/base/video_frame.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "mojo/public/cpp/base/shared_memory_utils.h" namespace mirroring {
diff --git a/components/mirroring/service/media_remoter.h b/components/mirroring/service/media_remoter.h index 1910da1..388a530 100644 --- a/components/mirroring/service/media_remoter.h +++ b/components/mirroring/service/media_remoter.h
@@ -9,6 +9,7 @@ #include "base/macros.h" #include "media/cast/cast_config.h" #include "media/mojo/interfaces/remoting.mojom.h" +#include "media/mojo/interfaces/remoting_common.mojom.h" #include "mojo/public/cpp/bindings/binding.h" namespace media {
diff --git a/components/mirroring/service/session.h b/components/mirroring/service/session.h index 7ceef26d..c70c62c 100644 --- a/components/mirroring/service/session.h +++ b/components/mirroring/service/session.h
@@ -23,6 +23,7 @@ #include "media/cast/cast_environment.h" #include "media/cast/net/cast_transport_defines.h" #include "media/mojo/interfaces/video_encode_accelerator.mojom.h" +#include "services/network/public/mojom/network_context.mojom.h" namespace media { class AudioInputDevice;
diff --git a/components/mirroring/service/video_capture_client.cc b/components/mirroring/service/video_capture_client.cc index 21da6e7..5da9e7f 100644 --- a/components/mirroring/service/video_capture_client.cc +++ b/components/mirroring/service/video_capture_client.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "media/base/bind_to_current_loop.h" #include "media/base/video_frame.h" +#include "media/capture/mojom/video_capture_types.mojom.h" namespace mirroring {
diff --git a/components/mirroring/service/video_capture_client_unittest.cc b/components/mirroring/service/video_capture_client_unittest.cc index 365c8c3..8006fc5 100644 --- a/components/mirroring/service/video_capture_client_unittest.cc +++ b/components/mirroring/service/video_capture_client_unittest.cc
@@ -12,6 +12,7 @@ #include "components/mirroring/service/fake_video_capture_host.h" #include "media/base/video_frame.h" #include "media/base/video_frame_metadata.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "mojo/public/cpp/base/shared_memory_utils.h" #include "mojo/public/cpp/bindings/binding.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc index b410cc7..967a875e 100644 --- a/components/payments/content/payment_request.cc +++ b/components/payments/content/payment_request.cc
@@ -32,6 +32,7 @@ namespace { +using ::payments::mojom::CanMakePaymentQueryResult; using ::payments::mojom::HasEnrolledInstrumentQueryResult; } // namespace @@ -357,7 +358,7 @@ } } -void PaymentRequest::CanMakePayment() { +void PaymentRequest::CanMakePayment(bool legacy_mode) { if (!IsInitialized()) { log_.Error("Attempted canMakePayment without initialization"); OnConnectionTerminated(); @@ -371,11 +372,12 @@ if (!delegate_->GetPrefService()->GetBoolean(kCanMakePaymentEnabled) || !state_) { - CanMakePaymentCallback(/*can_make_payment=*/false); + CanMakePaymentCallback(legacy_mode, /*can_make_payment=*/false); } else { state_->CanMakePayment( + legacy_mode, base::BindOnce(&PaymentRequest::CanMakePaymentCallback, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_.GetWeakPtr(), legacy_mode)); } } @@ -556,13 +558,33 @@ } } -void PaymentRequest::CanMakePaymentCallback(bool can_make_payment) { - client_->OnCanMakePayment( - can_make_payment ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT - : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT); +void PaymentRequest::CanMakePaymentCallback(bool legacy_mode, + bool can_make_payment) { + // Only need to enforce query quota in legacy mode. Per-method quota not + // supported. + if (legacy_mode && spec_ && + !CanMakePaymentQueryFactory::GetInstance() + ->GetForContext(web_contents_->GetBrowserContext()) + ->CanQuery(top_level_origin_, frame_origin_, + spec_->stringified_method_data(), + /*per_method_quota=*/false)) { + if (OriginSecurityChecker::IsOriginLocalhostOrFile(frame_origin_)) { + client_->OnCanMakePayment( + can_make_payment + ? CanMakePaymentQueryResult::WARNING_CAN_MAKE_PAYMENT + : CanMakePaymentQueryResult::WARNING_CANNOT_MAKE_PAYMENT); + } else { + client_->OnCanMakePayment( + CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED); + } + } else { + client_->OnCanMakePayment( + can_make_payment + ? mojom::CanMakePaymentQueryResult::CAN_MAKE_PAYMENT + : mojom::CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT); + } - // TODO(https://crbug.com/915907): emit JourneyLogger event once the event - // names are updated. + journey_logger_.SetCanMakePaymentValue(can_make_payment); if (observer_for_testing_) observer_for_testing_->OnCanMakePaymentReturned(); @@ -604,7 +626,7 @@ client_->OnHasEnrolledInstrument(has_enrolled_instrument ? positive : negative); - journey_logger_.SetCanMakePaymentValue(has_enrolled_instrument); + journey_logger_.SetHasEnrolledInstrumentValue(has_enrolled_instrument); } } // namespace payments
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h index 78b35a0c..ef4236b 100644 --- a/components/payments/content/payment_request.h +++ b/components/payments/content/payment_request.h
@@ -74,7 +74,7 @@ void NoUpdatedPaymentDetails() override; void Abort() override; void Complete(mojom::PaymentComplete result) override; - void CanMakePayment() override; + void CanMakePayment(bool legacy_mode) override; void HasEnrolledInstrument(bool per_method_quota) override; // PaymentRequestSpec::Observer: @@ -144,7 +144,7 @@ // The callback for PaymentRequestState::CanMakePayment. Checks for query // quota and may send QUERY_QUOTA_EXCEEDED. - void CanMakePaymentCallback(bool can_make_payment); + void CanMakePaymentCallback(bool legacy_mode, bool can_make_payment); // The callback for PaymentRequestState::HasEnrolledInstrument. Checks for // query quota and may send QUERY_QUOTA_EXCEEDED.
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc index abe7946a..0f6fda0 100644 --- a/components/payments/content/payment_request_state.cc +++ b/components/payments/content/payment_request_state.cc
@@ -149,8 +149,10 @@ NotifyOnGetAllPaymentInstrumentsFinished(); // Fulfill the pending CanMakePayment call. - if (can_make_payment_callback_) - CheckCanMakePayment(std::move(can_make_payment_callback_)); + if (can_make_payment_callback_) { + CheckCanMakePayment(can_make_payment_legacy_mode_, + std::move(can_make_payment_callback_)); + } // Fulfill the pending HasEnrolledInstrument call. if (has_enrolled_instrument_callback_) @@ -200,22 +202,38 @@ UpdateIsReadyToPayAndNotifyObservers(); } -void PaymentRequestState::CanMakePayment(StatusCallback callback) { +void PaymentRequestState::CanMakePayment(bool legacy_mode, + StatusCallback callback) { if (!get_all_instruments_finished_) { DCHECK(!can_make_payment_callback_); can_make_payment_callback_ = std::move(callback); + can_make_payment_legacy_mode_ = legacy_mode; return; } base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&PaymentRequestState::CheckCanMakePayment, - weak_ptr_factory_.GetWeakPtr(), std::move(callback))); + FROM_HERE, base::BindOnce(&PaymentRequestState::CheckCanMakePayment, + weak_ptr_factory_.GetWeakPtr(), legacy_mode, + std::move(callback))); } -void PaymentRequestState::CheckCanMakePayment(StatusCallback callback) { +void PaymentRequestState::CheckCanMakePayment(bool legacy_mode, + StatusCallback callback) { DCHECK(get_all_instruments_finished_); - std::move(callback).Run(are_requested_methods_supported_); + if (!legacy_mode) { + std::move(callback).Run(are_requested_methods_supported_); + return; + } + + // Legacy mode: fall back to also checking if an instrument is enrolled. + bool can_make_payment_value = false; + for (const auto& instrument : available_instruments_) { + if (instrument->IsValidForCanMakePayment()) { + can_make_payment_value = true; + break; + } + } + std::move(callback).Run(can_make_payment_value); } void PaymentRequestState::HasEnrolledInstrument(StatusCallback callback) { @@ -232,14 +250,8 @@ } void PaymentRequestState::CheckHasEnrolledInstrument(StatusCallback callback) { - bool has_enrolled_instrument_value = false; - for (const auto& instrument : available_instruments_) { - if (instrument->IsValidForCanMakePayment()) { - has_enrolled_instrument_value = true; - break; - } - } - std::move(callback).Run(has_enrolled_instrument_value); + // TODO(https://crbug.com/915907): Implement hasEnrolledInstrument. + NOTREACHED(); } void PaymentRequestState::AreRequestedMethodsSupported(
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h index 8923798..f149273 100644 --- a/components/payments/content/payment_request_state.h +++ b/components/payments/content/payment_request_state.h
@@ -99,7 +99,8 @@ // Checks whether support for the specified payment methods exist, either // because the user has a registered payment handler or because the browser // can do just-in-time registration for a suitable payment handler. - void CanMakePayment(StatusCallback callback); + // If |legacy_mode| is true, then also checks that an instrument is enrolled. + void CanMakePayment(bool legacy_mode, StatusCallback callback); // Checks whether the user has at least one instrument that satisfies the // specified supported payment methods asynchronously. @@ -263,7 +264,7 @@ // Checks whether support for the specified payment methods exists and call // the |callback| to return the result. - void CheckCanMakePayment(StatusCallback callback); + void CheckCanMakePayment(bool legacy_mode, StatusCallback callback); // Checks whether the user has at least one instrument that satisfies the // specified supported payment methods and call the |callback| to return the @@ -292,6 +293,10 @@ autofill::PersonalDataManager* personal_data_manager_; JourneyLogger* journey_logger_; + // Whether |can_make_payment_callback_| expects the legacy canMakePayment + // semantic. Only meaningful when |can_make_payment_callback_| is present. + bool can_make_payment_legacy_mode_ = false; + StatusCallback can_make_payment_callback_; StatusCallback has_enrolled_instrument_callback_; StatusCallback are_requested_methods_supported_callback_;
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc index 6a986456..ffbecc82 100644 --- a/components/payments/content/payment_request_state_unittest.cc +++ b/components/payments/content/payment_request_state_unittest.cc
@@ -140,20 +140,22 @@ autofill::CreditCard credit_card_visa_; }; -TEST_F(PaymentRequestStateTest, HasEnrolledInstrument) { +TEST_F(PaymentRequestStateTest, CanMakePayment) { // Default options. RecreateStateWithOptions(mojom::PaymentOptions::New()); - // HasEnrolledInstrument returns true because the method data requires Visa, + // Legacy CanMakePayment returns true because the method data requires Visa, // and the user has a Visa card on file. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_TRUE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment(/*legacy_mode=*/false, + base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } TEST_F(PaymentRequestStateTest, CanMakePayment_NoEnrolledInstrument) { @@ -166,17 +168,19 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns false because the method data requires + // Legacy CanMakePayment returns false because the method data requires // MasterCard, and the user doesn't have such an instrument. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_FALSE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_FALSE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported, even // though the payment instrument is not ready to pay. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment(/*legacy_mode=*/false, + base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } TEST_F(PaymentRequestStateTest, CanMakePayment_UnsupportedPaymentMethod) { @@ -188,20 +192,22 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns false because the method data requires + // Legacy CanMakePayment returns false because the method data requires // MasterCard, and the user doesn't have such an instrument. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_FALSE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_FALSE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported, even // though the payment instrument is not ready to pay. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_FALSE(can_make_payment); })); + state()->CanMakePayment(/*legacy_mode=*/false, + base::BindOnce([](bool can_make_payment) { + EXPECT_FALSE(can_make_payment); + })); } -TEST_F(PaymentRequestStateTest, HasEnrolledInstrument_OnlyBasicCard) { +TEST_F(PaymentRequestStateTest, CanMakePayment_OnlyBasicCard) { // The method data supports everything in basic-card. mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New(); entry->supported_method = "basic-card"; @@ -211,20 +217,21 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns true because the method data supports + // Legacy CanMakePayment returns true because the method data supports // everything, and the user has at least one instrument. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_TRUE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment( + /*legacy_mode=*/false, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } -TEST_F(PaymentRequestStateTest, - HasEnrolledInstrument_BasicCard_SpecificAvailable) { +TEST_F(PaymentRequestStateTest, CanMakePayment_BasicCard_SpecificAvailable) { // The method data supports visa through basic-card. mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New(); entry->supported_method = "basic-card"; @@ -235,20 +242,22 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns true because the method data supports visa, + // Legacy CanMakePayment returns true because the method data supports visa, // and the user has a Visa instrument. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_TRUE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment( + /*legacy_mode=*/false, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } TEST_F(PaymentRequestStateTest, - HasEnrolledInstrument_BasicCard_SpecificAvailableButInvalid) { + CanMakePayment_BasicCard_SpecificAvailableButInvalid) { // The method data supports jcb through basic-card. mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New(); entry->supported_method = "basic-card"; @@ -259,21 +268,22 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns false because the method data supports jcb, + // Legacy CanMakePayment returns false because the method data supports jcb, // and the user has a JCB instrument, but it's invalid. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_FALSE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_FALSE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported, even // though there is no enrolled instrument. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment( + /*legacy_mode=*/false, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } -TEST_F(PaymentRequestStateTest, - HasEnrolledInstrument_BasicCard_SpecificUnavailable) { +TEST_F(PaymentRequestStateTest, CanMakePayment_BasicCard_SpecificUnavailable) { // The method data supports mastercard through basic-card. mojom::PaymentMethodDataPtr entry = mojom::PaymentMethodData::New(); entry->supported_method = "basic-card"; @@ -284,17 +294,19 @@ mojom::PaymentDetails::New(), std::move(method_data)); - // HasEnrolledInstrument returns false because the method data supports + // Legacy CanMakePayment returns false because the method data supports // mastercard, and the user doesn't have such an instrument. - state()->HasEnrolledInstrument( - base::BindOnce([](bool has_enrolled_instrument) { - EXPECT_FALSE(has_enrolled_instrument); + state()->CanMakePayment( + /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) { + EXPECT_FALSE(can_make_payment); })); // CanMakePayment returns true because the requested method is supported, even // though there is no enrolled instrument. - state()->CanMakePayment(base::BindOnce( - [](bool can_make_payment) { EXPECT_TRUE(can_make_payment); })); + state()->CanMakePayment( + /*legacy_mode=*/false, base::BindOnce([](bool can_make_payment) { + EXPECT_TRUE(can_make_payment); + })); } TEST_F(PaymentRequestStateTest, ReadyToPay_DefaultSelections) {
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc index 307337f6..8b7ed73 100644 --- a/components/payments/core/journey_logger.cc +++ b/components/payments/core/journey_logger.cc
@@ -98,6 +98,14 @@ : EVENT_CAN_MAKE_PAYMENT_FALSE); } +void JourneyLogger::SetHasEnrolledInstrumentValue(bool value) { + if (is_incognito_) + return; + + SetEventOccurred(value ? EVENT_HAS_ENROLLED_INSTRUMENT_TRUE + : EVENT_HAS_ENROLLED_INSTRUMENT_FALSE); +} + void JourneyLogger::SetEventOccurred(Event event) { events_ |= event; }
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h index 7db5ce3..87e94f2 100644 --- a/components/payments/core/journey_logger.h +++ b/components/payments/core/journey_logger.h
@@ -91,6 +91,11 @@ EVENT_SELECTED_CREDIT_CARD = 1 << 18, EVENT_SELECTED_GOOGLE = 1 << 19, EVENT_SELECTED_OTHER = 1 << 20, + // hasEnrolledInstrument was called with a result of "true" or "false", + // respectively. An absence of both events means hasEnrolledInstrument was + // not called, or the user was in incognito mode. + EVENT_HAS_ENROLLED_INSTRUMENT_TRUE = 1 << 21, + EVENT_HAS_ENROLLED_INSTRUMENT_FALSE = 1 << 22, EVENT_ENUM_MAX = 2097152, }; @@ -140,10 +145,14 @@ int number, bool has_valid_suggestion); - // Records the fact that the merchant called CanMakePayment and records it's + // Records the fact that the merchant called CanMakePayment and records its // return value. void SetCanMakePaymentValue(bool value); + // Records the fact that the merchant called HasEnrolledInstrument and records + // its return value. + void SetHasEnrolledInstrumentValue(bool value); + // Records that an event occurred. void SetEventOccurred(Event event);
diff --git a/components/rlz/rlz_tracker.cc b/components/rlz/rlz_tracker.cc index 204a6c6..184cef8 100644 --- a/components/rlz/rlz_tracker.cc +++ b/components/rlz/rlz_tracker.cc
@@ -22,6 +22,7 @@ #include "build/build_config.h" #include "components/rlz/rlz_tracker_delegate.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/url_loader.mojom.h" #if defined(OS_CHROMEOS) #include "base/syslog_logging.h"
diff --git a/components/services/filesystem/directory_impl_unittest.cc b/components/services/filesystem/directory_impl_unittest.cc index ef2a1ae..3a5c9c3 100644 --- a/components/services/filesystem/directory_impl_unittest.cc +++ b/components/services/filesystem/directory_impl_unittest.cc
@@ -10,6 +10,7 @@ #include "base/stl_util.h" #include "components/services/filesystem/files_test_base.h" +#include "components/services/filesystem/public/interfaces/directory.mojom.h" namespace filesystem { namespace {
diff --git a/components/services/filesystem/file_impl_unittest.cc b/components/services/filesystem/file_impl_unittest.cc index 1aab2bb3..f43e30422 100644 --- a/components/services/filesystem/file_impl_unittest.cc +++ b/components/services/filesystem/file_impl_unittest.cc
@@ -9,6 +9,7 @@ #include "base/files/file.h" #include "components/services/filesystem/files_test_base.h" +#include "components/services/filesystem/public/interfaces/directory.mojom.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/type_converter.h"
diff --git a/components/services/heap_profiling/connection_manager.cc b/components/services/heap_profiling/connection_manager.cc index 2b8f8fd..2a115e1 100644 --- a/components/services/heap_profiling/connection_manager.cc +++ b/components/services/heap_profiling/connection_manager.cc
@@ -5,7 +5,6 @@ #include "components/services/heap_profiling/connection_manager.h" #include "base/bind.h" -#include "base/json/string_escape.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_current.h" #include "base/metrics/histogram_macros.h" @@ -75,15 +74,13 @@ scoped_refptr<ReceiverPipe> p, mojom::ProcessType process_type, uint32_t sampling_rate, - mojom::StackMode stack_mode, - bool stream_samples) + mojom::StackMode stack_mode) : thread(base::StringPrintf("Sender %lld thread", static_cast<long long>(pid))), client(std::move(client)), pipe(p), process_type(process_type), stack_mode(stack_mode), - stream_samples(stream_samples), tracker(std::move(complete_cb), backtrace_storage), sampling_rate(sampling_rate) {} @@ -106,7 +103,6 @@ scoped_refptr<StreamParser> parser; mojom::ProcessType process_type; mojom::StackMode stack_mode; - bool stream_samples; // Danger: This lives on the |thread| member above. The connection manager // lives on the I/O thread, so accesses to the variable must be synchronized. @@ -163,8 +159,7 @@ auto connection = std::make_unique<Connection>( std::move(complete_cb), &backtrace_storage_, pid, std::move(client), - new_pipe, process_type, params->sampling_rate, params->stack_mode, - params->stream_samples); + new_pipe, process_type, params->sampling_rate, params->stack_mode); base::Thread::Options options; options.message_loop_type = base::MessageLoop::TYPE_IO; @@ -259,13 +254,6 @@ for (auto& it : connections_) { base::ProcessId pid = it.first; Connection* connection = it.second.get(); - if (!connection->stream_samples) { - connection->client->RetrieveHeapProfile(base::BindOnce( - &ConnectionManager::HeapProfileRetrieved, weak_factory_.GetWeakPtr(), - tracking, pid, connection->process_type, keep_small_allocations, - strip_path_from_mapped_files, connection->sampling_rate)); - continue; - } int barrier_id = next_barrier_id_++; // Register for callback before requesting the dump so we don't race for the @@ -282,65 +270,6 @@ } } -void ConnectionManager::HeapProfileRetrieved( - scoped_refptr<DumpProcessesForTracingTracking> tracking, - base::ProcessId pid, - mojom::ProcessType process_type, - bool keep_small_allocations, - bool strip_path_from_mapped_files, - uint32_t sampling_rate, - mojom::HeapProfilePtr profile) { - AllocationCountMap counts; - AllocationTracker::ContextMap context_map; - AllocationTracker::AddressToStringMap string_map; - BacktraceStorage backtrace_storage; - BacktraceStorage::Lock backtrace_storage_lock(&backtrace_storage); - - bool success = true; - for (const mojom::HeapProfileSamplePtr& sample : profile->samples) { - int context_id = 0; - if (sample->context_id) { - auto it = profile->strings.find(sample->context_id); - if (it == profile->strings.end()) { - success = false; - break; - } - const std::string& context = it->second; - // Escape the strings early, to simplify exporting a heap dump. - std::string escaped_context; - base::EscapeJSONString(context, false /* put_in_quotes */, - &escaped_context); - context_id = context_map - .emplace(std::move(escaped_context), - static_cast<int>(context_map.size() + 1)) - .first->second; - } - const Backtrace* backtrace = backtrace_storage.Insert( - std::vector<Address>(sample->stack.begin(), sample->stack.end())); - AllocatorType allocator = static_cast<AllocatorType>(sample->allocator); - if (allocator >= AllocatorType::kCount) { - success = false; - break; - } - AllocationEvent alloc(allocator, Address(0), sample->size, backtrace, - context_id); - ++counts[alloc]; - } - - for (const auto& str : profile->strings) { - std::string quoted_string; - // Escape the strings before saving them, to simplify exporting a heap dump. - base::EscapeJSONString(str.second, false /* put_in_quotes */, - "ed_string); - string_map.emplace(str.first, std::move(quoted_string)); - } - - DoDumpOneProcessForTracing( - tracking, pid, process_type, keep_small_allocations, - strip_path_from_mapped_files, sampling_rate, success, std::move(counts), - std::move(context_map), std::move(string_map)); -} - void ConnectionManager::DoDumpOneProcessForTracing( scoped_refptr<DumpProcessesForTracingTracking> tracking, base::ProcessId pid,
diff --git a/components/services/heap_profiling/connection_manager.h b/components/services/heap_profiling/connection_manager.h index 6571f9f..6c746b3 100644 --- a/components/services/heap_profiling/connection_manager.h +++ b/components/services/heap_profiling/connection_manager.h
@@ -88,15 +88,6 @@ struct Connection; struct DumpProcessesForTracingTracking; - void HeapProfileRetrieved( - scoped_refptr<DumpProcessesForTracingTracking> tracking, - base::ProcessId pid, - mojom::ProcessType process_type, - bool keep_small_allocations, - bool strip_path_from_mapped_files, - uint32_t sampling_rate, - mojom::HeapProfilePtr profile); - void DoDumpOneProcessForTracing( scoped_refptr<DumpProcessesForTracingTracking> tracking, base::ProcessId pid,
diff --git a/components/services/heap_profiling/public/cpp/client.cc b/components/services/heap_profiling/public/cpp/client.cc index 39cd6c1d..020ef5e 100644 --- a/components/services/heap_profiling/public/cpp/client.cc +++ b/components/services/heap_profiling/public/cpp/client.cc
@@ -128,10 +128,6 @@ SamplingProfilerWrapper::FlushPipe(barrier_id); } -void Client::RetrieveHeapProfile(RetrieveHeapProfileCallback callback) { - std::move(callback).Run(sampling_profiler_->RetrieveHeapProfile()); -} - void Client::StartProfilingInternal(mojom::ProfilingParamsPtr params) { sampling_profiler_->StartProfiling(sender_pipe_.get(), std::move(params)); }
diff --git a/components/services/heap_profiling/public/cpp/client.h b/components/services/heap_profiling/public/cpp/client.h index 23abac2..e06103c7 100644 --- a/components/services/heap_profiling/public/cpp/client.h +++ b/components/services/heap_profiling/public/cpp/client.h
@@ -27,7 +27,6 @@ // mojom::ProfilingClient overrides: void StartProfiling(mojom::ProfilingParamsPtr params) override; void FlushMemlogPipe(uint32_t barrier_id) override; - void RetrieveHeapProfile(RetrieveHeapProfileCallback callback) override; void BindToInterface(mojom::ProfilingClientRequest request);
diff --git a/components/services/heap_profiling/public/cpp/controller.cc b/components/services/heap_profiling/public/cpp/controller.cc index 8ab69c5..afb48330 100644 --- a/components/services/heap_profiling/public/cpp/controller.cc +++ b/components/services/heap_profiling/public/cpp/controller.cc
@@ -16,12 +16,10 @@ Controller::Controller(std::unique_ptr<service_manager::Connector> connector, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate) : connector_(std::move(connector)), sampling_rate_(sampling_rate), stack_mode_(stack_mode), - stream_samples_(stream_samples), weak_factory_(this) { DCHECK_NE(sampling_rate, 0u); @@ -52,7 +50,6 @@ mojom::ProfilingParamsPtr params = mojom::ProfilingParams::New(); params->sampling_rate = sampling_rate_; - params->stream_samples = stream_samples_; params->sender_pipe = mojo::WrapPlatformHandle(pipes.PassSender()); params->stack_mode = stack_mode_; heap_profiling_service_->AddProfilingClient(
diff --git a/components/services/heap_profiling/public/cpp/controller.h b/components/services/heap_profiling/public/cpp/controller.h index 969f89e..ab077b0d 100644 --- a/components/services/heap_profiling/public/cpp/controller.h +++ b/components/services/heap_profiling/public/cpp/controller.h
@@ -42,7 +42,6 @@ // named |sampling_interval|. Controller(std::unique_ptr<service_manager::Connector> connector, mojom::StackMode stack_mode, - bool stream_samples, uint32_t sampling_rate); ~Controller(); @@ -72,7 +71,6 @@ // The same sampling rate and stack mode is used for each client. const uint32_t sampling_rate_ = 1; const mojom::StackMode stack_mode_; - const bool stream_samples_; SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<Controller> weak_factory_;
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc index 25fbc910..b97ec3c5 100644 --- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc +++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.cc
@@ -4,8 +4,6 @@ #include "components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h" -#include <utility> - #include "base/allocator/buildflags.h" #include "base/atomicops.h" #include "base/bind.h" @@ -426,12 +424,8 @@ *context = allocation_context.type_name; } -// Captures up to |max_entries| stack frames using the buffer pointed by -// |frames|. Puts the number of captured frames into the |count| output -// parameters. Returns the pointer to the topmost frame. -const void** CaptureStackTrace(const void** frames, - size_t max_entries, - size_t* count) { +void SerializeFramesFromBacktrace(FrameSerializer* serializer, + const char** context) { // Skip 3 top frames related to the profiler itself, e.g.: // base::debug::StackTrace::StackTrace // heap_profiling::RecordAndSendAlloc @@ -439,33 +433,26 @@ size_t skip_frames = 3; #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \ defined(OFFICIAL_BUILD) + const void* frames[kMaxStackEntries - 1]; size_t frame_count = base::trace_event::CFIBacktraceAndroid::GetInitializedInstance()->Unwind( - frames, max_entries); + frames, kMaxStackEntries - 1); #elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - size_t frame_count = - base::debug::TraceStackFramePointers(frames, max_entries, skip_frames); + const void* frames[kMaxStackEntries - 1]; + size_t frame_count = base::debug::TraceStackFramePointers( + frames, kMaxStackEntries - 1, skip_frames); skip_frames = 0; #else - // Fall-back to capturing the stack with base::debug::CollectStackTrace, + // Fall-back to capturing the stack with base::debug::StackTrace, // which is likely slower, but more reliable. - // TODO(alph): Make CollectStackTrace accept const void** pointer. - size_t frame_count = - base::debug::CollectStackTrace(const_cast<void**>(frames), max_entries); + base::debug::StackTrace stack_trace(kMaxStackEntries - 1); + size_t frame_count = 0u; + const void* const* frames = stack_trace.Addresses(&frame_count); #endif skip_frames = std::min(skip_frames, frame_count); - *count = frame_count - skip_frames; - return frames + skip_frames; -} - -void SerializeFramesFromBacktrace(FrameSerializer* serializer, - const char** context) { - const void* frames[kMaxStackEntries]; - size_t frames_count; - const void** first_frame = - CaptureStackTrace(frames, kMaxStackEntries - 1, &frames_count); - serializer->AddAllInstructionPointers(frames_count, first_frame); + serializer->AddAllInstructionPointers(frame_count - skip_frames, + frames + skip_frames); // Both thread name and task context require access to TLS. if (ScopedAllowAlloc::HasTLSBeenDestroyed()) @@ -618,14 +605,9 @@ base::PoissonAllocationSampler::Get()->RemoveSamplesObserver(this); } -SamplingProfilerWrapper::Sample::Sample() = default; -SamplingProfilerWrapper::Sample::Sample(Sample&&) = default; -SamplingProfilerWrapper::Sample::~Sample() = default; - void SamplingProfilerWrapper::StartProfiling(SenderPipe* sender_pipe, mojom::ProfilingParamsPtr params) { size_t sampling_rate = params->sampling_rate; - stream_samples_ = params->stream_samples; InitAllocationRecorder(sender_pipe, std::move(params)); auto* sampler = base::PoissonAllocationSampler::Get(); sampler->SetSamplingInterval(sampling_rate); @@ -637,128 +619,17 @@ base::PoissonAllocationSampler::Get()->Stop(); } -mojom::HeapProfilePtr SamplingProfilerWrapper::RetrieveHeapProfile() { - base::PoissonAllocationSampler::ScopedMuteThreadSamples no_samples_scope; - base::AutoLock lock(mutex_); - mojom::HeapProfilePtr profile = mojom::HeapProfile::New(); - profile->samples.reserve(samples_.size()); - for (const auto& pair : samples_) { - auto mojo_sample = mojom::HeapProfileSample::New(); - mojo_sample->allocator = static_cast<uint32_t>(pair.second.allocator); - mojo_sample->size = pair.second.size; - mojo_sample->context_id = reinterpret_cast<uintptr_t>(pair.second.context); - mojo_sample->stack = pair.second.stack; - profile->samples.push_back(std::move(mojo_sample)); - } - profile->strings.reserve(strings_.size()); - for (const char* string : strings_) - profile->strings.emplace(reinterpret_cast<uintptr_t>(string), string); - return profile; -} - -// The PoissonAllocationSampler that invokes this method guarantees -// non-reentrancy, i.e. no allocations made within the scope of SampleAdded -// will produce a sample. void SamplingProfilerWrapper::SampleAdded( void* address, size_t size, size_t total, base::PoissonAllocationSampler::AllocatorType type, const char* context) { - DCHECK(base::PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted()); - if (stream_samples_) { - RecordAndSendAlloc(ConvertType(type), address, size, context); - return; - } - base::AutoLock lock(mutex_); - Sample sample; - sample.allocator = ConvertType(type); - sample.size = size; - CaptureMode capture_mode = AllocationContextTracker::capture_mode(); - if (capture_mode == CaptureMode::PSEUDO_STACK || - capture_mode == CaptureMode::MIXED_STACK) { - CaptureMixedStack(context, &sample); - } else { - CaptureNativeStack(context, &sample); - } - samples_.emplace(address, std::move(sample)); -} - -void SamplingProfilerWrapper::CaptureMixedStack(const char* context, - Sample* sample) { - // Allocation context is tracked in TLS. Return nothing if TLS was destroyed. - if (ScopedAllowAlloc::HasTLSBeenDestroyed()) - return; - auto* tracker = AllocationContextTracker::GetInstanceForCurrentThread(); - if (!tracker) - return; - - AllocationContext allocation_context; - if (!tracker->GetContextSnapshot(&allocation_context)) - return; - - const base::trace_event::Backtrace& backtrace = allocation_context.backtrace; - CHECK_LE(backtrace.frame_count, kMaxStackEntries); - std::vector<uint64_t> stack; - stack.reserve(backtrace.frame_count); - for (int i = base::checked_cast<int>(backtrace.frame_count) - 1; i >= 0; - --i) { - const base::trace_event::StackFrame& frame = backtrace.frames[i]; - if (frame.type != base::trace_event::StackFrame::Type::PROGRAM_COUNTER) { - const char* name = static_cast<const char*>(frame.value); - if (strings_.find(name) == strings_.end()) - strings_.emplace(name); - } - stack.push_back(reinterpret_cast<uintptr_t>(frame.value)); - } - sample->stack = std::move(stack); - if (!context) - context = allocation_context.type_name; - sample->context = RecordString(context); -} - -void SamplingProfilerWrapper::CaptureNativeStack(const char* context, - Sample* sample) { - const void* stack[kMaxStackEntries]; - size_t frame_count; - // One frame is reserved for the thread name. - const void** first_frame = - CaptureStackTrace(stack, kMaxStackEntries - 1, &frame_count); - DCHECK_LT(frame_count, kMaxStackEntries); - sample->stack.reserve(frame_count + (g_include_thread_names ? 1 : 0)); - sample->stack.insert(sample->stack.end(), - reinterpret_cast<uint64_t*>(first_frame), - reinterpret_cast<uint64_t*>(first_frame + frame_count)); - - // Both thread name and task context require access to TLS. - if (ScopedAllowAlloc::HasTLSBeenDestroyed()) - return; - - if (g_include_thread_names) { - sample->stack.push_back( - reinterpret_cast<uintptr_t>(RecordString(GetOrSetThreadName()))); - } - if (!context) { - const auto* tracker = - AllocationContextTracker::GetInstanceForCurrentThread(); - if (tracker) - context = tracker->TaskContext(); - } - sample->context = RecordString(context); -} - -const char* SamplingProfilerWrapper::RecordString(const char* string) { - return string ? *strings_.insert(string).first : nullptr; + RecordAndSendAlloc(ConvertType(type), address, size, context); } void SamplingProfilerWrapper::SampleRemoved(void* address) { - DCHECK(base::PoissonAllocationSampler::ScopedMuteThreadSamples::IsMuted()); - if (stream_samples_) { - RecordAndSendFree(address); - return; - } - base::AutoLock lock(mutex_); - samples_.erase(address); + RecordAndSendFree(address); } } // namespace heap_profiling
diff --git a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h index 19d5a29c..bee5c72 100644 --- a/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h +++ b/components/services/heap_profiling/public/cpp/sampling_profiler_wrapper.h
@@ -5,10 +5,6 @@ #ifndef COMPONENTS_SERVICES_HEAP_PROFILING_PUBLIC_CPP_SAMPLING_PROFILER_WRAPPER_H_ #define COMPONENTS_SERVICES_HEAP_PROFILING_PUBLIC_CPP_SAMPLING_PROFILER_WRAPPER_H_ -#include <unordered_map> -#include <unordered_set> -#include <vector> - #include "base/sampling_heap_profiler/poisson_allocation_sampler.h" #include "components/services/heap_profiling/public/cpp/sender_pipe.h" #include "components/services/heap_profiling/public/cpp/stream.h" @@ -48,24 +44,7 @@ // logging process so it knows when this operation is complete. static void FlushPipe(uint32_t barrier_id); - mojom::HeapProfilePtr RetrieveHeapProfile(); - private: - struct Sample { - Sample(); - Sample(Sample&& sample); - ~Sample(); - - Sample& operator=(Sample&&) = default; - - AllocatorType allocator; - size_t size; - const char* context = nullptr; - std::vector<uint64_t> stack; - - DISALLOW_COPY_AND_ASSIGN(Sample); - }; - // base::PoissonAllocationSampler::SamplesObserver void SampleAdded(void* address, size_t size, @@ -73,26 +52,6 @@ base::PoissonAllocationSampler::AllocatorType, const char* context) override; void SampleRemoved(void* address) override; - - void CaptureMixedStack(const char* context, Sample* sample); - void CaptureNativeStack(const char* context, Sample* sample); - const char* RecordString(const char* string); - - bool stream_samples_ = false; - - // Mutex to access |samples_| and |strings_|. - base::Lock mutex_; - - // Samples of the currently live allocations. - std::unordered_map<void*, Sample> samples_; - - // When CaptureMode::PSEUDO_STACK or CaptureMode::MIXED_STACK is enabled - // the call stack contents of samples may contain strings besides - // PC addresses. - // In this case each string pointer is also added to the |strings_| set. - // The set does only contain pointers to static strings that are never - // deleted. - std::unordered_set<const char*> strings_; }; } // namespace heap_profiling
diff --git a/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom b/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom index 6a9607776..8b98d691 100644 --- a/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom +++ b/components/services/heap_profiling/public/mojom/heap_profiling_client.mojom
@@ -23,12 +23,6 @@ // A wrapper for parameters that affect each client's implementation of // profiling. struct ProfilingParams { - // When |stream_samples| is true the samples are streamed through the - // provided |sender_pipe|. Otherwise, the samples are stored on - // the client side during recording and can be retrieved using - // |ProfilingClient.RetrieveHeapProfile| method. - bool stream_samples; - // The client should record allocations into |memlog_sender_pipe|. handle sender_pipe; @@ -43,48 +37,17 @@ uint32 sampling_rate; }; -// A single memory allocation sample. -struct HeapProfileSample { - // Allocator type. - uint32 allocator; - - // The size in bytes accounted for the sample. - uint64 size; - - // Id of the context string. - uint64 context_id; - - // Program stack in top to bottom order recorded for the allocation. - // Each element of the |stack| is either a PC memory address or a string - // if it is among the |strings| map items of |HeapProfile|. - array<uint64> stack; -}; - -// Heap profile data. Can be retrieved from the client with -// |RetrieveHeapProfile| method. -struct HeapProfile { - // Samples recorded for the profile. - array<HeapProfileSample> samples; - - // Strings used within the profile. - map<uint64, string> strings; -}; - // This interface is implemented by "memlog clients" (profiled processes that // can send memory allocation events to the profiling process). These functions // are called by the profiling process to control the senders. interface ProfilingClient { - // Start recording allocations. - // Collected allocations are either streamed to the profiling process via - // |params.sender_pipe|, or accumulated in the profiled process depending on - // the |params.use_in_process_storage|. - // There is currently no mechanism to stop recording allocations. + // Start recording allocations and sending them to the profiling process via + // |params.sender_pipe|. There is currently no mechanism to stop recording + // allocations. StartProfiling(ProfilingParams params); // Flushes the memlog pipe associated with this client. A barrier packet is // set over the memlog pipe with the given identifier. This allows the // receiver to synchronize with the flush. FlushMemlogPipe(uint32 barrier_id); - - RetrieveHeapProfile() => (HeapProfile profile); };
diff --git a/components/viz/host/DEPS b/components/viz/host/DEPS index d8f55bb..1a6b0e0 100644 --- a/components/viz/host/DEPS +++ b/components/viz/host/DEPS
@@ -11,6 +11,7 @@ "+gpu/ipc/client", "+gpu/ipc/common", "+gpu/ipc/host", + "+media/capture/mojom/video_capture_types.mojom.h", "+mojo/public/cpp", "+services/viz/privileged/interfaces", "+services/viz/public/interfaces",
diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc index 7cae6723..a09ce3e 100644 --- a/components/viz/host/client_frame_sink_video_capturer.cc +++ b/components/viz/host/client_frame_sink_video_capturer.cc
@@ -3,10 +3,12 @@ // found in the LICENSE file. #include "components/viz/host/client_frame_sink_video_capturer.h" -#include "base/bind.h" #include <utility> +#include "base/bind.h" +#include "media/capture/mojom/video_capture_types.mojom.h" + namespace viz { namespace {
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc index 64ab61e..e718f6d 100644 --- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc +++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -20,6 +20,7 @@ #include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_manager.h" #include "media/base/limits.h" #include "media/base/video_util.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/devtools/devtools_video_consumer.cc b/content/browser/devtools/devtools_video_consumer.cc index 3c67a3b4..75acd6e 100644 --- a/content/browser/devtools/devtools_video_consumer.cc +++ b/content/browser/devtools/devtools_video_consumer.cc
@@ -13,6 +13,7 @@ #include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h" #include "content/browser/compositor/surface_utils.h" #include "media/base/limits.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "media/renderers/paint_canvas_video_renderer.h" namespace content {
diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc index ab4fad3..c95590f 100644 --- a/content/browser/devtools/devtools_video_consumer_unittest.cc +++ b/content/browser/devtools/devtools_video_consumer_unittest.cc
@@ -11,6 +11,7 @@ #include "content/browser/devtools/devtools_video_consumer.h" #include "content/public/test/test_utils.h" #include "media/base/limits.h" +#include "media/capture/mojom/video_capture_types.mojom.h" #include "mojo/public/cpp/base/shared_memory_utils.h" #include "mojo/public/cpp/bindings/binding.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h index 47341c8..f88f1fc 100644 --- a/content/browser/frame_host/navigation_handle_impl.h +++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -541,7 +541,7 @@ ThrottleChecksFinishedCallback complete_callback_for_testing_; // Manages the lifetime of a pre-created ServiceWorkerProviderHost until a - // corresponding ServiceWorkerNetworkProvider is created in the renderer. + // corresponding provider is created in the renderer. std::unique_ptr<ServiceWorkerNavigationHandle> service_worker_handle_; // Manages the lifetime of a pre-created AppCacheHost until a browser side
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc index 120fbeb..c51de6a 100644 --- a/content/browser/media/session/media_session_impl_browsertest.cc +++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -818,8 +818,10 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - ControlsShowForTransientAndPlaybackStatePaused) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +IN_PROC_BROWSER_TEST_P( + MediaSessionImplParamBrowserTest, + DISABLED_ControlsShowForTransientAndPlaybackStatePaused) { EnsureMediaSessionService(); auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( shell()->web_contents()->GetMainFrame()); @@ -842,8 +844,10 @@ EXPECT_TRUE(IsActive()); } -IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - ControlsShowForTransientAndPlaybackStatePlaying) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +IN_PROC_BROWSER_TEST_P( + MediaSessionImplParamBrowserTest, + DISABLED_ControlsShowForTransientAndPlaybackStatePlaying) { EnsureMediaSessionService(); auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>( shell()->web_contents()->GetMainFrame()); @@ -1491,8 +1495,9 @@ EXPECT_TRUE(IsActive()); } +// TODO(https://crbug.com/925868): Fix and re-enable this. IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - ControlsDontShowWhenOneShotIsPresent) { + DISABLED_ControlsDontShowWhenOneShotIsPresent) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); { @@ -1533,8 +1538,10 @@ } } -IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - ControlsHiddenAfterRemoveOneShotWithoutOtherPlayers) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +IN_PROC_BROWSER_TEST_P( + MediaSessionImplParamBrowserTest, + DISABLED_ControlsHiddenAfterRemoveOneShotWithoutOtherPlayers) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); { @@ -1559,8 +1566,10 @@ EXPECT_FALSE(IsActive()); } -IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - ControlsShowAfterRemoveOneShotWithPersistentPresent) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +IN_PROC_BROWSER_TEST_P( + MediaSessionImplParamBrowserTest, + DISABLED_ControlsShowAfterRemoveOneShotWithPersistentPresent) { auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(); { @@ -2181,8 +2190,10 @@ EXPECT_EQ(expected_metadata, observer.WaitForMetadata()); } -IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest, - AddingObserverNotifiesCurrentInformation_WithInfo) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +IN_PROC_BROWSER_TEST_P( + MediaSessionImplParamBrowserTest, + DISABLED_AddingMojoObserverNotifiesCurrentInformation_WithInfo) { // Set up the service and information. EnsureMediaSessionService();
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc index b4c3d73..c25285d 100644 --- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc +++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -286,8 +286,9 @@ EXPECT_TRUE(observer.WaitForMetadata()->IsEmpty()); } +// TODO(https://crbug.com/925868): Fix and re-enable this. TEST_F(MediaSessionImplServiceRoutingTest, - NotifyMetadataAndActionsChangeWhenControllable) { + DISABLED_NotifyMetadataAndActionsChangeWhenControllable) { media_session::MediaMetadata expected_metadata; expected_metadata.title = base::ASCIIToUTF16("title"); expected_metadata.artist = base::ASCIIToUTF16("artist"); @@ -322,8 +323,9 @@ } } +// TODO(https://crbug.com/925868): Fix and re-enable this. TEST_F(MediaSessionImplServiceRoutingTest, - NotifyMetadataAndActionsChangeWhenTurningControllable) { + DISABLED_NotifyMetadataAndActionsChangeWhenTurningControllable) { media_session::MediaMetadata expected_metadata; expected_metadata.title = base::ASCIIToUTF16("title"); expected_metadata.artist = base::ASCIIToUTF16("artist"); @@ -359,8 +361,9 @@ } } +// TODO(https://crbug.com/925868): Fix and re-enable this. TEST_F(MediaSessionImplServiceRoutingTest, - NotifyActionsAndMetadataChangeWhenTurningUncontrollable) { + DISABLED_NotifyActionsAndMetadataChangeWhenTurningUncontrollable) { media_session::MediaMetadata expected_metadata; expected_metadata.title = base::ASCIIToUTF16("title"); expected_metadata.artist = base::ASCIIToUTF16("artist"); @@ -659,8 +662,9 @@ observer.actions_set()); } +// TODO(https://crbug.com/925868): Fix and re-enable this. TEST_F(MediaSessionImplServiceRoutingTest, - DefaultActionsRemovedIfUncontrollable) { + DISABLED_DefaultActionsRemovedIfUncontrollable) { CreateServiceForFrame(main_frame_); StartPlayerForFrame(main_frame_, media::MediaContentType::OneShot);
diff --git a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h index 01fb38b..1a7799ba 100644 --- a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h +++ b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory.h
@@ -25,6 +25,7 @@ #include "media/audio/audio_input_delegate.h" #include "media/mojo/interfaces/audio_logging.mojom.h" #include "mojo/public/cpp/bindings/binding.h" +#include "services/audio/public/mojom/audio_processing.mojom.h" namespace media { class AudioParameters;
diff --git a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory_unittest.cc b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory_unittest.cc index 76f0cae..5fc0454 100644 --- a/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory_unittest.cc +++ b/content/browser/renderer_host/media/old_render_frame_audio_input_stream_factory_unittest.cc
@@ -22,8 +22,10 @@ #include "media/audio/mock_audio_manager.h" #include "media/audio/test_audio_thread.h" #include "media/base/audio_parameters.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/platform_handle.h" +#include "services/audio/public/mojom/audio_processing.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory_unittest.cc b/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory_unittest.cc index 8277d2d..b3f6212 100644 --- a/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory_unittest.cc +++ b/content/browser/renderer_host/media/old_render_frame_audio_output_stream_factory_unittest.cc
@@ -18,6 +18,7 @@ #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "media/base/audio_parameters.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/platform_handle.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc index 6719d24..d8efbcd6 100644 --- a/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc +++ b/content/browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc
@@ -31,6 +31,7 @@ #include "media/audio/fake_audio_manager.h" #include "media/audio/test_audio_thread.h" #include "media/base/audio_parameters.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "services/audio/public/cpp/fake_stream_factory.h"
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc index 93cdd6d..7220d9d 100644 --- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc +++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -19,6 +19,7 @@ #include "services/video_capture/public/cpp/mock_device_factory.h" #include "services/video_capture/public/cpp/mock_device_factory_provider.h" #include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/mojom/producer.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/renderer_host/native_web_keyboard_event_mac.mm b/content/browser/renderer_host/native_web_keyboard_event_mac.mm index 4d48c96..9ac6998 100644 --- a/content/browser/renderer_host/native_web_keyboard_event_mac.mm +++ b/content/browser/renderer_host/native_web_keyboard_event_mac.mm
@@ -62,7 +62,11 @@ size_t unmod_text_length = WebKeyboardEventTextLength(web_event.unmodified_text); - if (text_length == 0) + // Perform the reverse operation on type that was done in + // UnmodifiedTextFromEvent(). Avoid using text_length as the control key may + // cause Mac to set [NSEvent characters] to "\0" which for us is + // indistinguishable from "". + if (unmod_text_length == 0) type = NSFlagsChanged; NSString* text =
diff --git a/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm b/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm new file mode 100644 index 0000000..f30a913 --- /dev/null +++ b/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm
@@ -0,0 +1,42 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "content/public/browser/native_web_keyboard_event.h" + +#import <AppKit/AppKit.h> +#import <Carbon/Carbon.h> + +#include "base/mac/scoped_nsobject.h" +#include "content/browser/renderer_host/input/web_input_event_builders_mac.h" +#import "testing/gtest_mac.h" +#include "ui/events/blink/web_input_event.h" + +// Going from NSEvent to WebKeyboardEvent and back should round trip. +TEST(NativeWebKeyboardEventMac, CtrlCmdSpaceKeyDownRoundTrip) { + base::scoped_nsobject<NSWindow> window([NSWindow alloc]); + + NSEvent* ns_event = + [[NSEvent keyEventWithType:NSEventTypeKeyDown + location:NSZeroPoint + modifierFlags:NSEventModifierFlagControl | + NSEventModifierFlagCommand + timestamp:0 + windowNumber:[window windowNumber] + context:nil + characters:@"\0" // The control modifier results in + // ' ' being bitmasked with 0x1F + // which == 0x00. + charactersIgnoringModifiers:@" " + isARepeat:NO + keyCode:kVK_Space] retain]; + blink::WebKeyboardEvent web_event = + content::WebKeyboardEventBuilder::Build(ns_event); + content::NativeWebKeyboardEvent native_event( + web_event, gfx::NativeView([window contentView])); + + NSEvent* round_trip_ns_event = native_event.os_event; + EXPECT_EQ([round_trip_ns_event type], [ns_event type]); + EXPECT_EQ([round_trip_ns_event modifierFlags], [ns_event modifierFlags]); + EXPECT_EQ([round_trip_ns_event keyCode], [ns_event keyCode]); +}
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index fcab5b30..90c0a16 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -233,6 +233,11 @@ #include "content/browser/compositor/image_transport_factory.h" #endif +#if defined(OS_LINUX) +#include <sys/resource.h> +#include <sys/time.h> +#endif + #if defined(OS_MACOSX) #include "content/browser/mach_broker_mac.h" #endif @@ -1264,6 +1269,28 @@ base::BindOnce(&AddCorbExceptionForPluginOnUIThread, process_id)); } +#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) +static constexpr size_t kUnknownPlatformProcessLimit = 0; + +// Returns the process limit from the system. Use |kUnknownPlatformProcessLimit| +// to indicate failure and std::numeric_limits<size_t>::max() to indicate +// unlimited. +size_t GetPlatformProcessLimit() { +#if defined(OS_LINUX) + struct rlimit limit; + if (getrlimit(RLIMIT_NPROC, &limit) != 0) + return kUnknownPlatformProcessLimit; + + if (limit.rlim_cur == RLIM_INFINITY) + return std::numeric_limits<size_t>::max(); + return base::saturated_cast<size_t>(limit.rlim_cur); +#else + // TODO(https://crbug.com/104689): Implement on other platforms. + return kUnknownPlatformProcessLimit; +#endif // defined(OS_LINUX) +} +#endif // !defined(OS_ANDROID) && !defined(OS_CHROMEOS) + } // namespace // Held by the RPH and used to control an (unowned) ConnectionFilterImpl from @@ -1369,8 +1396,15 @@ #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) // static size_t RenderProcessHostImpl::GetPlatformMaxRendererProcessCount() { - static constexpr size_t kMaxRendererProcessCount = 82; - return kMaxRendererProcessCount; + // Set the limit to half of the system limit to leave room for other programs. + size_t limit = GetPlatformProcessLimit() / 2; + + // If the system limit is unavailable, use a fallback value instead. + if (limit == kUnknownPlatformProcessLimit) { + static constexpr size_t kMaxRendererProcessCount = 82; + limit = kMaxRendererProcessCount; + } + return limit; } #endif @@ -1425,9 +1459,12 @@ max_count /= kEstimatedWebContentsMemoryUsage; static constexpr size_t kMinRendererProcessCount = 3; - max_count = base::ClampToRange( - max_count, kMinRendererProcessCount, - RenderProcessHostImpl::GetPlatformMaxRendererProcessCount()); + static const size_t kMaxRendererProcessCount = + RenderProcessHostImpl::GetPlatformMaxRendererProcessCount(); + DCHECK_LE(kMinRendererProcessCount, kMaxRendererProcessCount); + + max_count = base::ClampToRange(max_count, kMinRendererProcessCount, + kMaxRendererProcessCount); } return max_count; #endif
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h index 7bc59f08..27e5c470 100644 --- a/content/browser/renderer_host/render_view_host_delegate.h +++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -15,6 +15,7 @@ #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/common/content_export.h" #include "content/common/render_message_filter.mojom.h" +#include "content/common/widget.mojom.h" #include "net/base/load_states.h" class GURL;
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h index 4f15e953..d3d6b42 100644 --- a/content/browser/service_worker/service_worker_provider_host.h +++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -132,7 +132,7 @@ using ExecutionReadyCallback = base::OnceClosure; // Used to pre-create a ServiceWorkerProviderHost for a navigation. The - // ServiceWorkerNetworkProvider will later be created in the renderer, should + // ServiceWorkerProviderContext will later be created in the renderer, should // the navigation succeed. |are_ancestors_secure| should be true for main // frames. Otherwise it is true iff all ancestor frames of this frame have a // secure origin. |web_contents_getter| indicates the tab where the navigation @@ -197,10 +197,10 @@ // Returns whether this provider host is secure enough to have a service // worker controller. - // Analogous to Blink's Document::isSecureContext. Because of how service + // Analogous to Blink's Document::IsSecureContext. Because of how service // worker intercepts main resource requests, this check must be done // browser-side once the URL is known (see comments in - // ServiceWorkerNetworkProvider::CreateForNavigation). This function uses + // WebServiceWorkerNetworkProviderImplForFrame::Create). This function uses // |url_| and |is_parent_frame_secure_| to determine context security, so they // must be set properly before calling this function. bool IsContextSecureForServiceWorker() const;
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.h b/content/browser/service_worker/service_worker_script_loader_factory.h index 85781d9..ca286493 100644 --- a/content/browser/service_worker/service_worker_script_loader_factory.h +++ b/content/browser/service_worker/service_worker_script_loader_factory.h
@@ -21,10 +21,11 @@ // S13nServiceWorker: // Created per one running service worker for loading its scripts. This is kept -// alive while ServiceWorkerNetworkProvider in the renderer process is alive. +// alive while the WebServiceWorkerNetworkProvider in the renderer process is +// alive. // -// This factory handles requests for scripts from service workers that were -// new (non-installed) when they started. For service workers that were already +// This factory handles requests for scripts from service workers that were new +// (non-installed) when they started. For service workers that were already // installed when they started, ServiceWorkerInstalledScriptsManager is used // instead. //
diff --git a/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc b/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc index e1c39730..55b9b36a 100644 --- a/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc +++ b/content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.cc
@@ -12,6 +12,7 @@ #include "content/public/common/content_features.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/mojom/url_loader.mojom.h" namespace content {
diff --git a/content/common/service_worker/embedded_worker.mojom b/content/common/service_worker/embedded_worker.mojom index e113968..88bb5a3b 100644 --- a/content/common/service_worker/embedded_worker.mojom +++ b/content/common/service_worker/embedded_worker.mojom
@@ -70,7 +70,7 @@ blink.mojom.ServiceWorkerInstalledScriptsInfo? installed_scripts_info; // Interface for the renderer to send the status updates to the browser. associated EmbeddedWorkerInstanceHost instance_host; - // Information for creating ServiceWorkerNetworkProvider on the renderer. + // Information for creating ServiceWorkerProviderContext on the renderer. blink.mojom.ServiceWorkerProviderInfoForStartWorker provider_info; // Interface for the renderer to query the content settings in the browser. blink.mojom.WorkerContentSettingsProxy content_settings_proxy;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 88b4c986..ce6ce32 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -532,8 +532,6 @@ "service_worker/service_worker_type_converters.h", "service_worker/service_worker_type_util.cc", "service_worker/service_worker_type_util.h", - "service_worker/web_service_worker_network_provider_base_impl.cc", - "service_worker/web_service_worker_network_provider_base_impl.h", "service_worker/web_service_worker_network_provider_impl_for_frame.cc", "service_worker/web_service_worker_network_provider_impl_for_frame.h", "service_worker/web_service_worker_provider_impl.cc",
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc index 974f65ce..c8e78c1 100644 --- a/content/renderer/loader/web_worker_fetch_context_impl.cc +++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -28,9 +28,9 @@ #include "content/renderer/loader/web_url_loader_impl.h" #include "content/renderer/loader/web_url_request_util.h" #include "content/renderer/service_worker/controller_service_worker_connector.h" -#include "content/renderer/service_worker/service_worker_network_provider.h" #include "content/renderer/service_worker/service_worker_provider_context.h" #include "content/renderer/service_worker/service_worker_subresource_loader.h" +#include "content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" @@ -119,8 +119,8 @@ private: bool CanCreateServiceWorkerURLLoader(const blink::WebURLRequest& request) { // TODO(horo): Unify this code path with - // ServiceWorkerNetworkProvider::CreateURLLoader that is used for document - // cases. + // WebServiceWorkerNetworkProviderImplForFrame::CreateURLLoader that is used + // for document cases. // We need the service worker loader factory populated in order to create // our own URLLoader for subresource loading via a service worker. @@ -153,13 +153,12 @@ }; scoped_refptr<WebWorkerFetchContextImpl> WebWorkerFetchContextImpl::Create( - ServiceWorkerNetworkProvider* network_provider, + ServiceWorkerProviderContext* provider_context, blink::mojom::RendererPreferences renderer_preferences, blink::mojom::RendererPreferenceWatcherRequest watcher_request, std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info, std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info) { - DCHECK(network_provider); blink::mojom::ServiceWorkerWorkerClientRequest service_worker_client_request; blink::mojom::ServiceWorkerWorkerClientRegistryPtrInfo service_worker_worker_client_registry_ptr_info; @@ -167,7 +166,6 @@ // Some sandboxed iframes are not allowed to use service worker so don't have // a real service worker provider, so the provider context is null. - ServiceWorkerProviderContext* provider_context = network_provider->context(); if (provider_context) { provider_context->CloneWorkerClientRegistry( mojo::MakeRequest(&service_worker_worker_client_registry_ptr_info)); @@ -194,12 +192,18 @@ ->CreateWebSocketHandshakeThrottleProvider(), ChildThreadImpl::current()->thread_safe_sender(), ChildThreadImpl::current()->GetConnector()->Clone())); - worker_fetch_context->set_service_worker_provider_id( - network_provider->provider_id()); - worker_fetch_context->set_is_controlled_by_service_worker( - network_provider->IsControlledByServiceWorker()); - if (provider_context) + if (provider_context) { + worker_fetch_context->set_service_worker_provider_id( + provider_context->provider_id()); + worker_fetch_context->set_is_controlled_by_service_worker( + provider_context->IsControlledByServiceWorker()); worker_fetch_context->set_client_id(provider_context->client_id()); + } else { + worker_fetch_context->set_service_worker_provider_id( + kInvalidServiceWorkerProviderId); + worker_fetch_context->set_is_controlled_by_service_worker( + blink::mojom::ControllerServiceWorkerMode::kNoController); + } return worker_fetch_context; }
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h index 11e2fc9b..88b05dc 100644 --- a/content/renderer/loader/web_worker_fetch_context_impl.h +++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -20,6 +20,7 @@ #include "third_party/blink/public/mojom/blob/blob_registry.mojom.h" #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h" #include "third_party/blink/public/mojom/renderer_preferences.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h" #include "third_party/blink/public/platform/web_application_cache_host.h" @@ -35,7 +36,7 @@ class FrameRequestBlocker; class ResourceDispatcher; -class ServiceWorkerNetworkProvider; +class ServiceWorkerProviderContext; class ThreadSafeSender; class URLLoaderThrottleProvider; class WebSocketHandshakeThrottleProvider; @@ -51,8 +52,8 @@ public: // Creates a new fetch context for a worker. // - // |network_provider| is the ServiceWorkerNetworkProvider of the worker and is - // used to route requests to its controller service worker. + // |provider_context| is used to route requests to the worker's controller + // service worker. // |loader_factory_info| is used for regular loading by the worker. // // S13nServiceWorker: @@ -68,7 +69,7 @@ // because it might additionally support non-NetworkService schemes (e.g., // chrome-extension://). static scoped_refptr<WebWorkerFetchContextImpl> Create( - ServiceWorkerNetworkProvider* network_provider, + ServiceWorkerProviderContext* provider_context, blink::mojom::RendererPreferences renderer_preferences, blink::mojom::RendererPreferenceWatcherRequest watcher_request, std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc index 7b939ec..15b479a 100644 --- a/content/renderer/mus/renderer_window_tree_client.cc +++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -18,6 +18,7 @@ #include "content/renderer/mus/mus_embedded_frame.h" #include "content/renderer/mus/mus_embedded_frame_delegate.h" #include "content/renderer/render_frame_proxy.h" +#include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/base/ui_base_features.h" namespace content {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 87d6fc2..0a9b65d 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -143,7 +143,7 @@ #include "content/renderer/renderer_webapplicationcachehost_impl.h" #include "content/renderer/resource_timing_info_conversions.h" #include "content/renderer/savable_resources.h" -#include "content/renderer/service_worker/service_worker_network_provider.h" +#include "content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h" #include "content/renderer/service_worker/web_service_worker_provider_impl.h" #include "content/renderer/skia_benchmarking_extension.h" #include "content/renderer/stats_collection_controller.h" @@ -3702,12 +3702,10 @@ scoped_refptr<blink::WebWorkerFetchContext> RenderFrameImpl::CreateWorkerFetchContext() { - blink::WebServiceWorkerNetworkProvider* web_provider = - frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider(); - DCHECK(web_provider); - ServiceWorkerNetworkProvider* provider = - ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( - web_provider); + WebServiceWorkerNetworkProviderImplForFrame* provider = + static_cast<WebServiceWorkerNetworkProviderImplForFrame*>( + frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider()); + DCHECK(provider); blink::mojom::RendererPreferenceWatcherPtr watcher; blink::mojom::RendererPreferenceWatcherRequest watcher_request = @@ -3716,7 +3714,7 @@ scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context = WebWorkerFetchContextImpl::Create( - provider, render_view_->renderer_preferences(), + provider->context(), render_view_->renderer_preferences(), std::move(watcher_request), GetLoaderFactoryBundle()->Clone(), GetLoaderFactoryBundle()->CloneWithoutAppCacheFactory()); @@ -3780,8 +3778,8 @@ // At this point we should have non-null data source. if (!ChildThreadImpl::current()) return nullptr; // May be null in some tests. - ServiceWorkerNetworkProvider* provider = - ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( + WebServiceWorkerNetworkProviderImplForFrame* provider = + static_cast<WebServiceWorkerNetworkProviderImplForFrame*>( frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider()); if (!provider->context()) { // The context can be null when the frame is sandboxed. @@ -7177,18 +7175,6 @@ #endif } -blink::mojom::ControllerServiceWorkerMode -RenderFrameImpl::IsControlledByServiceWorker() { - blink::WebServiceWorkerNetworkProvider* web_provider = - frame_->GetDocumentLoader()->GetServiceWorkerNetworkProvider(); - if (!web_provider) - return blink::mojom::ControllerServiceWorkerMode::kNoController; - ServiceWorkerNetworkProvider* provider = - ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( - web_provider); - return provider->IsControlledByServiceWorker(); -} - void RenderFrameImpl::BindWidget(mojom::WidgetRequest request) { GetLocalRootRenderWidget()->SetWidgetBinding(std::move(request)); } @@ -7251,7 +7237,7 @@ scoped_refptr<network::SharedURLLoaderFactory> fallback_factory = network::SharedURLLoaderFactory::Create( GetLoaderFactoryBundle()->CloneWithoutAppCacheFactory()); - return ServiceWorkerNetworkProvider::CreateForNavigation( + return WebServiceWorkerNetworkProviderImplForFrame::Create( this, commit_params, std::move(controller_service_worker_info), std::move(fallback_factory)); }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 1ef0b77a..20bc4c9 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -1237,9 +1237,6 @@ void ShowDeferredContextMenu(const ContextMenuParams& params); - // Whether the frame is controlled by a service worker. - blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker(); - // Build DidCommitProvisionalLoad_Params based on the frame internal state. std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> MakeDidCommitProvisionalLoadParams(blink::WebHistoryCommitType commit_type,
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index ad1fa7d..5174eb1 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -2163,8 +2163,7 @@ void RenderThreadImpl::SetUpEmbeddedWorkerChannelForServiceWorker( mojom::EmbeddedWorkerInstanceClientRequest client_request) { - EmbeddedWorkerInstanceClientImpl::Create(GetIOTaskRunner(), - std::move(client_request)); + EmbeddedWorkerInstanceClientImpl::Create(std::move(client_request)); } void RenderThreadImpl::OnNetworkConnectionChanged(
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc index 9cdecad..4a94050 100644 --- a/content/renderer/render_view_browsertest.cc +++ b/content/renderer/render_view_browsertest.cc
@@ -51,7 +51,7 @@ #include "content/renderer/render_frame_proxy.h" #include "content/renderer/render_process.h" #include "content/renderer/render_view_impl.h" -#include "content/renderer/service_worker/service_worker_network_provider.h" +#include "content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_browser_context.h" #include "content/test/fake_compositor_dependencies.h" @@ -2159,30 +2159,23 @@ // Service workers require https. GURL example_url("https://example.com"); - blink::WebServiceWorkerNetworkProvider* webprovider = nullptr; - ServiceWorkerNetworkProvider* provider = nullptr; + WebServiceWorkerNetworkProviderImplForFrame* provider = nullptr; RequestExtraData* extra_data = nullptr; // Make sure each new document has a new provider and // that the main request is tagged with the provider's id. LoadHTMLWithUrlOverride("<b>A Document</b>", example_url.spec().c_str()); ASSERT_TRUE(GetMainFrame()->GetDocumentLoader()); - webprovider = - GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider(); - ASSERT_TRUE(webprovider); - provider = ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( - webprovider); + provider = static_cast<WebServiceWorkerNetworkProviderImplForFrame*>( + GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider()); ASSERT_TRUE(provider); int provider1_id = provider->provider_id(); LoadHTMLWithUrlOverride("<b>New Document B Goes Here</b>", example_url.spec().c_str()); ASSERT_TRUE(GetMainFrame()->GetDocumentLoader()); - webprovider = - GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider(); - ASSERT_TRUE(provider); - provider = ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( - webprovider); + provider = static_cast<WebServiceWorkerNetworkProviderImplForFrame*>( + GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider()); ASSERT_TRUE(provider); EXPECT_NE(provider1_id, provider->provider_id()); @@ -2191,7 +2184,7 @@ blink::WebURLRequest request(GURL("http://foo.com")); request.SetRequestContext(blink::mojom::RequestContextType::SUBRESOURCE); blink::WebURLResponse redirect_response; - webprovider->WillSendRequest(request); + provider->WillSendRequest(request); extra_data = static_cast<RequestExtraData*>(request.GetExtraData()); ASSERT_TRUE(extra_data); EXPECT_EQ(extra_data->service_worker_provider_id(), provider->provider_id());
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc index 3ba445f..2ec6f562 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/metrics/histogram_macros.h" #include "base/strings/utf_string_conversions.h" +#include "content/child/child_thread_impl.h" #include "content/child/scoped_child_process_reference.h" #include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/content_client.h" @@ -27,20 +28,20 @@ // static void EmbeddedWorkerInstanceClientImpl::Create( - scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner, mojom::EmbeddedWorkerInstanceClientRequest request) { // This won't be leaked because the lifetime will be managed internally. // See the class documentation for detail. - new EmbeddedWorkerInstanceClientImpl(std::move(io_thread_runner), - std::move(request)); + // We can't use MakeStrongBinding because must give the worker thread + // a chance to stop by calling TerminateWorkerContext() and waiting + // before destructing. + new EmbeddedWorkerInstanceClientImpl(std::move(request)); } void EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed() { DCHECK(worker_); TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed"); - // Destroys |this|. - worker_.reset(); + delete this; } void EmbeddedWorkerInstanceClientImpl::StartWorker( @@ -69,8 +70,8 @@ std::move(params->renderer_preferences), std::move(params->service_worker_request), std::move(params->controller_request), std::move(params->instance_host), - std::move(params->provider_info), std::move(temporal_self_), - std::move(start_timing), std::move(params->preference_watcher_request), + std::move(params->provider_info), this, std::move(start_timing), + std::move(params->preference_watcher_request), std::move(params->subresource_loader_factories), RenderThreadImpl::current() ->GetWebMainThreadScheduler() @@ -89,11 +90,11 @@ void EmbeddedWorkerInstanceClientImpl::StopWorker() { // StopWorker must be called after StartWorker is called. - DCHECK(ChildThreadImpl::current()); DCHECK(worker_); TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerInstanceClientImpl::StopWorker"); worker_->TerminateWorkerContext(); + // We continue in WorkerContextDestroyed() after the worker thread is stopped. } void EmbeddedWorkerInstanceClientImpl::ResumeAfterDownload() { @@ -117,11 +118,8 @@ } EmbeddedWorkerInstanceClientImpl::EmbeddedWorkerInstanceClientImpl( - scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner, mojom::EmbeddedWorkerInstanceClientRequest request) - : binding_(this, std::move(request)), - temporal_self_(this), - io_thread_runner_(std::move(io_thread_runner)) { + : binding_(this, std::move(request)) { binding_.set_connection_error_handler(base::BindOnce( &EmbeddedWorkerInstanceClientImpl::OnError, base::Unretained(this))); } @@ -129,9 +127,16 @@ EmbeddedWorkerInstanceClientImpl::~EmbeddedWorkerInstanceClientImpl() {} void EmbeddedWorkerInstanceClientImpl::OnError() { - // Destroys |this| if |temporal_self_| still owns this (i.e., StartWorker() - // was not yet called). - temporal_self_.reset(); + // The connection to the browser process broke. + if (worker_) { + // The worker is running, so tell it to stop. We continue in + // WorkerContextDestroyed(). + StopWorker(); + return; + } + + // Nothing left to do. + delete this; } std::unique_ptr<blink::WebEmbeddedWorker>
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h index 12524e4..3304d28 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -7,9 +7,8 @@ #include <memory> -#include "base/containers/id_map.h" -#include "base/single_thread_task_runner.h" #include "content/child/child_thread_impl.h" +#include "content/common/content_export.h" #include "content/common/service_worker/embedded_worker.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "third_party/blink/public/common/privacy_preferences.h" @@ -26,24 +25,14 @@ class ServiceWorkerContextClient; -// This class exposes interfaces of WebEmbeddedWorker to the browser process. -// Unless otherwise noted, all methods should be called on the main thread. // EmbeddedWorkerInstanceClientImpl is created in order to start a service -// worker, and lives as long as the service worker is running. +// worker. The browser processes sends a Mojo request that creates an instance +// of this class. The instance deletes itself when the service worker stops. If +// the Mojo connection to the browser breaks first, the instance waits for the +// service worker to stop and then deletes itself. // -// This class deletes itself when the worker stops (or if start failed). The -// ownership graph is a cycle like this: -// EmbeddedWorkerInstanceClientImpl -(owns)-> WorkerWrapper -(owns)-> -// WebEmbeddedWorkerImpl -(owns)-> ServiceWorkerContextClient -(owns)-> -// EmbeddedWorkerInstanceClientImpl. Therefore, an instance can delete itself by -// releasing its WorkerWrapper. -// -// Since starting/stopping service workers is initiated by the browser process, -// the browser process effectively controls the lifetime of this class. -// -// TODO(shimazu): Let EmbeddedWorkerInstanceClientImpl own itself instead of -// the big reference cycle. -class EmbeddedWorkerInstanceClientImpl +// All methods are called on the main thread. +class CONTENT_EXPORT EmbeddedWorkerInstanceClientImpl : public mojom::EmbeddedWorkerInstanceClient { public: // Enum for UMA to record when StartWorker is received. @@ -58,13 +47,11 @@ // documentation. // TODO(shimazu): Create a service worker's execution context by this method // instead of just creating an instance of EmbeddedWorkerInstanceClient. - static void Create( - scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner, - mojom::EmbeddedWorkerInstanceClientRequest request); + static void Create(mojom::EmbeddedWorkerInstanceClientRequest request); ~EmbeddedWorkerInstanceClientImpl() override; - // Called from ServiceWorkerContextClient. + // Destroys |this|. Called from ServiceWorkerContextClient. void WorkerContextDestroyed(); // mojom::EmbeddedWorkerInstanceClient implementation (partially exposed to @@ -72,8 +59,9 @@ void StopWorker() override; private: - EmbeddedWorkerInstanceClientImpl( - scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner, + friend class ServiceWorkerContextClientTest; + + explicit EmbeddedWorkerInstanceClientImpl( mojom::EmbeddedWorkerInstanceClientRequest request); // mojom::EmbeddedWorkerInstanceClient implementation @@ -97,15 +85,9 @@ mojo::Binding<mojom::EmbeddedWorkerInstanceClient> binding_; - // This is valid before StartWorker is called. After that, this object - // will be passed to ServiceWorkerContextClient. - std::unique_ptr<EmbeddedWorkerInstanceClientImpl> temporal_self_; - // nullptr means the worker is not running. std::unique_ptr<blink::WebEmbeddedWorker> worker_; - scoped_refptr<base::SingleThreadTaskRunner> io_thread_runner_; - DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceClientImpl); };
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc index 6cac5c3..2f7c3a2 100644 --- a/content/renderer/service_worker/service_worker_context_client.cc +++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -523,7 +523,7 @@ blink::mojom::ControllerServiceWorkerRequest controller_request, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, - std::unique_ptr<EmbeddedWorkerInstanceClientImpl> embedded_worker_client, + EmbeddedWorkerInstanceClientImpl* owner, mojom::EmbeddedWorkerStartTimingPtr start_timing, blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request, std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders, @@ -539,8 +539,10 @@ proxy_(nullptr), pending_service_worker_request_(std::move(service_worker_request)), pending_controller_request_(std::move(controller_request)), - embedded_worker_client_(std::move(embedded_worker_client)), + owner_(owner), start_timing_(std::move(start_timing)) { + DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence()); + DCHECK(owner_); instance_host_ = mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr::Create( std::move(instance_host), main_thread_task_runner_); @@ -579,6 +581,7 @@ } ServiceWorkerContextClient::~ServiceWorkerContextClient() { + DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence()); // TODO(crbug.com/907311): Remove this instrumentation after we identified // the cause of crash. if (report_debug_log_ && context_) { @@ -607,8 +610,7 @@ TRACE_EVENT_NESTABLE_ASYNC_END1("ServiceWorker", "ServiceWorkerContextClient", this, "Status", "WorkerContextFailedToStart"); - DCHECK(embedded_worker_client_); - embedded_worker_client_->WorkerContextDestroyed(); + owner_->WorkerContextDestroyed(); } void ServiceWorkerContextClient::FailedToLoadInstalledClassicScript() { @@ -639,10 +641,15 @@ } void ServiceWorkerContextClient::WorkerScriptLoaded() { - // This could be called both from the main thread and worker thread as the - // comment in WebServiceWorkerContextClient describes. - if (!is_starting_installed_worker_) - (*instance_host_)->OnScriptLoaded(); + DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence()); + DCHECK(!is_starting_installed_worker_); + (*instance_host_)->OnScriptLoaded(); + TRACE_EVENT_NESTABLE_ASYNC_END0("ServiceWorker", "LOAD_SCRIPT", this); +} + +void ServiceWorkerContextClient::InstalledWorkerScriptLoaded() { + DCHECK(worker_task_runner_->RunsTasksInCurrentSequence()); + DCHECK(is_starting_installed_worker_); TRACE_EVENT_NESTABLE_ASYNC_END0("ServiceWorker", "LOAD_SCRIPT", this); } @@ -760,26 +767,14 @@ DCHECK(g_worker_client_tls.Pointer()->Get() == nullptr); RecordDebugLog("WorkerContextDestroyed"); - // TODO(shimazu): The signals to the browser should be in the order: - // (1) WorkerStopped (via mojo call EmbeddedWorkerInstanceHost.OnStopped()) - // (2) ProviderDestroyed (via mojo call - // ServiceWorkerDispatcherHost.OnProviderDestroyed()), this is triggered by - // the following EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed(), - // which will eventually lead to destruction of the service worker provider. - // But currently EmbeddedWorkerInstanceHost interface is associated with - // EmbeddedWorkerInstanceClient interface, and ServiceWorkerDispatcherHost - // interface is associated with the IPC channel, since they are using - // different mojo message pipes, the FIFO ordering can not be guaranteed now. - // This will be solved once ServiceWorkerProvider{Host,Client} are mojoified - // and they are also associated with EmbeddedWorkerInstanceClient in other CLs - // (https://crrev.com/2653493009 and https://crrev.com/2779763004). (*instance_host_)->OnStopped(); - DCHECK(embedded_worker_client_); + // base::Unretained is safe because |owner_| does not destroy itself until + // WorkerContextDestroyed is called. main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&EmbeddedWorkerInstanceClientImpl::WorkerContextDestroyed, - std::move(embedded_worker_client_))); + base::Unretained(owner_))); } void ServiceWorkerContextClient::CountFeature( @@ -1882,8 +1877,7 @@ void ServiceWorkerContextClient::StopWorker() { RecordDebugLog("StopWorker"); DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence()); - if (embedded_worker_client_) - embedded_worker_client_->StopWorker(); + owner_->StopWorker(); } base::WeakPtr<ServiceWorkerContextClient>
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h index 04ae5a3..4f991d88 100644 --- a/content/renderer/service_worker/service_worker_context_client.h +++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -62,10 +62,11 @@ // context. It enables communication between the embedder and Blink's // ServiceWorkerGlobalScope. It is created when the service worker begins // starting up, and destroyed when the service worker stops. It is owned by -// EmbeddedWorkerInstanceClientImpl's internal WorkerWrapper class. +// WebEmbeddedWorkerImpl (which is owned by EmbeddedWorkerInstanceClientImpl). // -// Unless otherwise noted (here or in base class documentation), all methods are -// called on the worker thread. +// This class is created and destroyed on the main thread. Unless otherwise +// noted (here or in base class documentation), all methods are called on the +// worker thread. class CONTENT_EXPORT ServiceWorkerContextClient : public blink::WebServiceWorkerContextClient, public blink::mojom::ServiceWorker { @@ -75,12 +76,12 @@ static ServiceWorkerContextClient* ThreadSpecificInstance(); // Called on the main thread. - // |is_starting_installed_worker| is true if the script is already installed - // and will be streamed from the browser process. - // - // |start_timing| should be initially populated with - // |start_worker_received_time|. This instance will fill in the rest during - // startup. + // - |is_starting_installed_worker| is true if the script is already installed + // and will be streamed from the browser process. + // - |owner| must outlive this new instance. + // - |start_timing| should be initially populated with + // |start_worker_received_time|. This instance will fill in the rest during + // startup. ServiceWorkerContextClient( int embedded_worker_id, int64_t service_worker_version_id, @@ -92,11 +93,12 @@ blink::mojom::ControllerServiceWorkerRequest controller_request, mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host, blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info, - std::unique_ptr<EmbeddedWorkerInstanceClientImpl> embedded_worker_client, + EmbeddedWorkerInstanceClientImpl* owner, mojom::EmbeddedWorkerStartTimingPtr start_timing, blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request, std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders, scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner); + // Called on the main thread. ~ServiceWorkerContextClient() override; // WebServiceWorkerContextClient overrides. @@ -105,6 +107,7 @@ void FailedToLoadInstalledClassicScript() override; void FailedToFetchModuleScript() override; void WorkerScriptLoaded() override; + void InstalledWorkerScriptLoaded() override; void WorkerContextStarted( blink::WebServiceWorkerContextProxy* proxy) override; void WillEvaluateScript() override; @@ -404,8 +407,8 @@ blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr service_worker_provider_info_; - // This is valid from the ctor to WorkerContextDestroyed. - std::unique_ptr<EmbeddedWorkerInstanceClientImpl> embedded_worker_client_; + // Must be accessed on the main thread only. + EmbeddedWorkerInstanceClientImpl* owner_; blink::mojom::BlobRegistryPtr blob_registry_;
diff --git a/content/renderer/service_worker/service_worker_context_client_unittest.cc b/content/renderer/service_worker/service_worker_context_client_unittest.cc index 6d4cc63..d0950b4 100644 --- a/content/renderer/service_worker/service_worker_context_client_unittest.cc +++ b/content/renderer/service_worker/service_worker_context_client_unittest.cc
@@ -15,6 +15,7 @@ #include "base/time/tick_clock.h" #include "base/time/time.h" #include "content/child/thread_safe_sender.h" +#include "content/common/service_worker/embedded_worker.mojom.h" #include "content/common/service_worker/service_worker_types.h" #include "content/public/common/content_client.h" #include "content/public/renderer/content_renderer_client.h" @@ -43,6 +44,7 @@ #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h" +#include "third_party/blink/public/web/web_embedded_worker.h" namespace content { @@ -50,6 +52,9 @@ // Pipes connected to the context client. struct ContextClientPipes { + // From the browser to EmbeddedWorkerInstanceClientImpl. + mojom::EmbeddedWorkerInstanceClientPtr embedded_worker_instance_client; + // From the browser to ServiceWorkerContextClient. blink::mojom::ServiceWorkerPtr service_worker; blink::mojom::ControllerServiceWorkerPtr controller; @@ -63,6 +68,29 @@ registration_host_request; }; +// A fake WebEmbeddedWorker. This is needed to ensure WorkerContextDestroyed() +// is called on EmbeddedWorkerInstanceClientImpl so it can self-destruct. +class FakeWebEmbeddedWorker : public blink::WebEmbeddedWorker { + public: + explicit FakeWebEmbeddedWorker( + std::unique_ptr<ServiceWorkerContextClient> context_client) + : context_client_(std::move(context_client)) {} + + void StartWorkerContext(const blink::WebEmbeddedWorkerStartData&) override {} + void TerminateWorkerContext() override { + v8::Local<v8::Context> local; + context_client_->WillDestroyWorkerContext(local); + context_client_->WorkerContextDestroyed(); + } + void ResumeAfterDownload() override {} + void AddMessageToConsole(const blink::WebConsoleMessage&) override {} + void BindDevToolsAgent(mojo::ScopedInterfaceEndpointHandle, + mojo::ScopedInterfaceEndpointHandle) override {} + + private: + std::unique_ptr<ServiceWorkerContextClient> context_client_; +}; + class MockWebServiceWorkerContextProxy : public blink::WebServiceWorkerContextProxy { public: @@ -240,6 +268,7 @@ message_loop_.SetTaskRunner(task_runner_); // Use this thread as the worker thread. WorkerThreadRegistry::Instance()->DidStartCurrentWorkerThread(); + old_client_ = SetRendererClientForTesting(&renderer_client_); } void TearDown() override { @@ -247,6 +276,7 @@ // Unregister this thread from worker threads. WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); task_runner_->RunUntilIdle(); + SetRendererClientForTesting(old_client_); } void EnableServicification() { @@ -264,9 +294,16 @@ // Creates an ContextClient, whose pipes are connected to |out_pipes|, then // simulates that the service worker thread has started with |proxy|. - std::unique_ptr<ServiceWorkerContextClient> CreateContextClient( + // + // The client's lifetime is tied to + // |out_pipes->embedded_worker_instance_client|. + ServiceWorkerContextClient* CreateContextClient( ContextClientPipes* out_pipes, blink::WebServiceWorkerContextProxy* proxy) { + EmbeddedWorkerInstanceClientImpl* embedded_worker_instance_client = + new EmbeddedWorkerInstanceClientImpl( + mojo::MakeRequest(&out_pipes->embedded_worker_instance_client)); + auto service_worker_request = mojo::MakeRequest(&out_pipes->service_worker); auto controller_request = mojo::MakeRequest(&out_pipes->controller); mojom::EmbeddedWorkerInstanceHostAssociatedPtr embedded_worker_host_ptr; @@ -274,21 +311,23 @@ mojo::MakeRequestAssociatedWithDedicatedPipe(&embedded_worker_host_ptr); const GURL kScope("https://example.com"); const GURL kScript("https://example.com/SW.js"); - std::unique_ptr<ServiceWorkerContextClient> context_client = - std::make_unique<ServiceWorkerContextClient>( - 1 /* embedded_worker_id */, 1 /* service_worker_version_id */, - kScope, kScript, false /* is_script_streaming */, - blink::mojom::RendererPreferences::New(), - std::move(service_worker_request), std::move(controller_request), - embedded_worker_host_ptr.PassInterface(), CreateProviderInfo(), - nullptr /* embedded_worker_client */, - mojom::EmbeddedWorkerStartTiming::New(), - nullptr /* preference_watcher_request */, - nullptr /* subresource_loaders */, - blink::scheduler::GetSingleThreadTaskRunnerForTesting()); + auto context_client = std::make_unique<ServiceWorkerContextClient>( + 1 /* embedded_worker_id */, 1 /* service_worker_version_id */, kScope, + kScript, false /* is_script_streaming */, + blink::mojom::RendererPreferences::New(), + std::move(service_worker_request), std::move(controller_request), + embedded_worker_host_ptr.PassInterface(), CreateProviderInfo(), + embedded_worker_instance_client, + mojom::EmbeddedWorkerStartTiming::New(), + nullptr /* preference_watcher_request */, + nullptr /* subresource_loaders */, + blink::scheduler::GetSingleThreadTaskRunnerForTesting()); + auto* context_client_raw = context_client.get(); context_client->SetReportDebugLogForTesting(false); - context_client->WorkerContextStarted(proxy); + embedded_worker_instance_client->worker_ = + std::make_unique<FakeWebEmbeddedWorker>(std::move(context_client)); + context_client_raw->WorkerContextStarted(proxy); blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host; out_pipes->service_worker_host_request = @@ -305,13 +344,13 @@ out_pipes->service_worker->InitializeGlobalScope( std::move(service_worker_host), std::move(registration_info)); task_runner()->RunUntilIdle(); - return context_client; + return context_client_raw; } - std::unique_ptr<ServiceWorkerContextClient> CreateIdleContextClient( + ServiceWorkerContextClient* CreateIdleContextClient( ContextClientPipes* pipes, MockWebServiceWorkerContextProxy* mock_proxy) { - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateContextClient(pipes, mock_proxy); context_client->DidEvaluateScript(true /* success */); task_runner()->RunUntilIdle(); @@ -341,13 +380,14 @@ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; base::test::ScopedFeatureList feature_list_; bool is_idle_; + ContentRendererClient renderer_client_; + ContentRendererClient* old_client_; }; TEST_F(ServiceWorkerContextClientTest, Ping) { ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = - CreateContextClient(&pipes, &mock_proxy); + CreateContextClient(&pipes, &mock_proxy); bool is_called = false; pipes.service_worker->Ping(CreateCallbackWithCalledFlag(&is_called)); @@ -358,7 +398,7 @@ TEST_F(ServiceWorkerContextClientTest, DispatchFetchEvent) { ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateContextClient(&pipes, &mock_proxy); context_client->DidEvaluateScript(true /* success */); task_runner()->RunUntilIdle(); @@ -395,7 +435,7 @@ ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateContextClient(&pipes, &mock_proxy); context_client->DidEvaluateScript(true /* success */); task_runner()->RunUntilIdle(); @@ -436,7 +476,7 @@ EnableServicification(); ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateContextClient(&pipes, &mock_proxy); context_client->DidEvaluateScript(true /* success */); task_runner()->RunUntilIdle(); @@ -473,8 +513,7 @@ EnableServicification(); ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = - CreateIdleContextClient(&pipes, &mock_proxy); + CreateIdleContextClient(&pipes, &mock_proxy); const GURL expected_url("https://example.com/expected"); @@ -496,7 +535,8 @@ EXPECT_TRUE(mock_proxy.fetch_events().empty()); // Destruction of |context_client| should not hit any DCHECKs. - context_client.reset(); + pipes.embedded_worker_instance_client.reset(); + task_runner()->RunUntilIdle(); } TEST_F(ServiceWorkerContextClientTest, @@ -504,7 +544,7 @@ EnableServicification(); ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateIdleContextClient(&pipes, &mock_proxy); const GURL expected_url_1("https://example.com/expected_1"); @@ -558,7 +598,7 @@ EnableServicification(); ContextClientPipes pipes; MockWebServiceWorkerContextProxy mock_proxy; - std::unique_ptr<ServiceWorkerContextClient> context_client = + ServiceWorkerContextClient* context_client = CreateIdleContextClient(&pipes, &mock_proxy); int task_id = context_client->WillStartTask();
diff --git a/content/renderer/service_worker/service_worker_network_provider.cc b/content/renderer/service_worker/service_worker_network_provider.cc index 6727910..1f90baeb 100644 --- a/content/renderer/service_worker/service_worker_network_provider.cc +++ b/content/renderer/service_worker/service_worker_network_provider.cc
@@ -5,238 +5,13 @@ #include "content/renderer/service_worker/service_worker_network_provider.h" #include "base/atomic_sequence_num.h" -#include "content/child/child_thread_impl.h" -#include "content/common/navigation_params.h" -#include "content/common/service_worker/service_worker_utils.h" -#include "content/renderer/render_frame_impl.h" -#include "content/renderer/renderer_blink_platform_impl.h" -#include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h" -#include "content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h" -#include "ipc/ipc_sync_channel.h" -#include "mojo/public/cpp/bindings/associated_group.h" -#include "services/network/public/cpp/shared_url_loader_factory.h" -#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" -#include "third_party/blink/public/common/frame/sandbox_flags.h" -#include "third_party/blink/public/common/service_worker/service_worker_utils.h" -#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" -#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h" -#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" -#include "third_party/blink/public/platform/web_security_origin.h" -#include "third_party/blink/public/web/web_local_frame.h" namespace content { -namespace { - // Must be unique in the child process. -int GetNextProviderId() { +int GetNextServiceWorkerProviderId() { static base::AtomicSequenceNumber sequence; return sequence.GetNext(); // We start at zero. } -// Returns whether it's possible for a document whose frame is a descendant of -// |frame| to be a secure context, not considering scheme exceptions (since any -// document can be a secure context if it has a scheme exception). See -// Document::isSecureContextImpl for more details. -bool IsFrameSecure(blink::WebFrame* frame) { - while (frame) { - if (!frame->GetSecurityOrigin().IsPotentiallyTrustworthy()) - return false; - frame = frame->Parent(); - } - return true; -} - -} // namespace - -std::unique_ptr<blink::WebServiceWorkerNetworkProvider> -ServiceWorkerNetworkProvider::CreateInvalidInstanceForNavigation() { - return std::make_unique<WebServiceWorkerNetworkProviderImplForFrame>( - base::WrapUnique(new ServiceWorkerNetworkProvider()), nullptr); -} - -// static -std::unique_ptr<blink::WebServiceWorkerNetworkProvider> -ServiceWorkerNetworkProvider::CreateForNavigation( - RenderFrameImpl* frame, - const CommitNavigationParams* commit_params, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) { - blink::WebLocalFrame* web_frame = frame->GetWebFrame(); - // Determine if a ServiceWorkerNetworkProvider should be created and properly - // initialized for the navigation. A default ServiceWorkerNetworkProvider - // will always be created since it is expected in a certain number of places, - // however it will have an invalid id. - bool should_create_provider = false; - int provider_id = kInvalidServiceWorkerProviderId; - if (commit_params) { - should_create_provider = commit_params->should_create_service_worker; - provider_id = commit_params->service_worker_provider_id; - } else { - // It'd be convenient to check web_frame->GetSecurityOrigin().IsOpaque() - // here instead of just looking at the sandbox flags, but - // GetSecurityOrigin() crashes because the frame does not yet have a - // security context. - should_create_provider = - ((web_frame->EffectiveSandboxFlags() & - blink::WebSandboxFlags::kOrigin) != blink::WebSandboxFlags::kOrigin); - } - - // If we shouldn't create a real ServiceWorkerNetworkProvider, return one with - // an invalid id. - if (!should_create_provider) { - return CreateInvalidInstanceForNavigation(); - } - - // Otherwise, create the ServiceWorkerNetworkProvider. - - // Ideally Document::IsSecureContext would be called here, but the document is - // not created yet, and due to redirects the URL may change. So pass - // is_parent_frame_secure to the browser process, so it can determine the - // context security when deciding whether to allow a service worker to control - // the document. - const bool is_parent_frame_secure = IsFrameSecure(web_frame->Parent()); - - // If the browser process did not assign a provider id already, assign one - // now (see class comments for content::ServiceWorkerProviderHost). - DCHECK(ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id) || - provider_id == kInvalidServiceWorkerProviderId); - if (provider_id == kInvalidServiceWorkerProviderId) - provider_id = GetNextProviderId(); - - auto provider = base::WrapUnique(new ServiceWorkerNetworkProvider( - frame->GetRoutingID(), - blink::mojom::ServiceWorkerProviderType::kForWindow, provider_id, - is_parent_frame_secure, std::move(controller_info), - std::move(fallback_loader_factory))); - return std::make_unique<WebServiceWorkerNetworkProviderImplForFrame>( - std::move(provider), frame); -} - -// static -std::unique_ptr<ServiceWorkerNetworkProvider> -ServiceWorkerNetworkProvider::CreateForSharedWorker( - blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_info, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) { - // S13nServiceWorker: |info| holds info about the precreated provider host. - if (info) { - DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled()); - return base::WrapUnique(new ServiceWorkerNetworkProvider( - std::move(info), std::move(script_loader_factory_info), - std::move(controller_info), std::move(fallback_loader_factory))); - } - - return base::WrapUnique(new ServiceWorkerNetworkProvider( - MSG_ROUTING_NONE, - blink::mojom::ServiceWorkerProviderType::kForSharedWorker, - GetNextProviderId(), true /* is_parent_frame_secure */, - nullptr /* controller_service_worker */, - std::move(fallback_loader_factory))); -} - -// static -ServiceWorkerNetworkProvider* -ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( - blink::WebServiceWorkerNetworkProvider* provider) { - if (!provider) { - DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled()); - return nullptr; - } - return static_cast<WebServiceWorkerNetworkProviderBaseImpl*>(provider) - ->provider(); -} - -ServiceWorkerNetworkProvider::~ServiceWorkerNetworkProvider() { - if (context()) { - context()->OnNetworkProviderDestroyed(); - } -} - -int ServiceWorkerNetworkProvider::provider_id() const { - if (!context()) - return kInvalidServiceWorkerProviderId; - return context()->provider_id(); -} - -blink::mojom::ControllerServiceWorkerMode -ServiceWorkerNetworkProvider::IsControlledByServiceWorker() const { - if (!context()) - return blink::mojom::ControllerServiceWorkerMode::kNoController; - return context()->IsControlledByServiceWorker(); -} - -void ServiceWorkerNetworkProvider::DispatchNetworkQuiet() { - if (!context()) - return; - context()->DispatchNetworkQuiet(); -} - -// Creates an invalid instance (provider_id() returns -// kInvalidServiceWorkerProviderId). -ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider() {} - -// Constructor for service worker clients. -ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider( - int route_id, - blink::mojom::ServiceWorkerProviderType provider_type, - int provider_id, - bool is_parent_frame_secure, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) { - DCHECK_NE(provider_id, kInvalidServiceWorkerProviderId); - DCHECK(provider_type == blink::mojom::ServiceWorkerProviderType::kForWindow || - provider_type == - blink::mojom::ServiceWorkerProviderType::kForSharedWorker); - - auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New( - provider_id, route_id, provider_type, is_parent_frame_secure, - nullptr /* host_request */, nullptr /* client_ptr_info */); - blink::mojom::ServiceWorkerContainerAssociatedRequest client_request = - mojo::MakeRequest(&host_info->client_ptr_info); - blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; - host_info->host_request = mojo::MakeRequest(&host_ptr_info); - DCHECK(host_info->host_request.is_pending()); - DCHECK(host_info->host_request.handle().is_valid()); - - context_ = base::MakeRefCounted<ServiceWorkerProviderContext>( - provider_id, provider_type, std::move(client_request), - std::move(host_ptr_info), std::move(controller_info), - std::move(fallback_loader_factory)); - - if (ChildThreadImpl::current()) { - ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( - &dispatcher_host_); - dispatcher_host_->OnProviderCreated(std::move(host_info)); - } else { - // current() may be null in tests. Silently drop messages sent over - // ServiceWorkerContainerHost since we couldn't set it up correctly due to - // this test limitation. This way we don't crash when the associated - // interface ptr is used. - // - // TODO(falken): Just give SWPC a null interface ptr and make the callsites - // deal with it. They are supposed to anyway because - // OnNetworkProviderDestroyed() can reset the ptr to null at any time. - mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle()); - } -} - -// Constructor for precreated shared worker. -ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider( - blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_info, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) { - context_ = base::MakeRefCounted<ServiceWorkerProviderContext>( - info->provider_id, - blink::mojom::ServiceWorkerProviderType::kForSharedWorker, - std::move(info->client_request), std::move(info->host_ptr_info), - std::move(controller_info), std::move(fallback_loader_factory)); - if (script_loader_factory_info.is_valid()) - script_loader_factory_.Bind(std::move(script_loader_factory_info)); -} - } // namespace content
diff --git a/content/renderer/service_worker/service_worker_network_provider.h b/content/renderer/service_worker/service_worker_network_provider.h index 9d7d57d2..c6dde50 100644 --- a/content/renderer/service_worker/service_worker_network_provider.h +++ b/content/renderer/service_worker/service_worker_network_provider.h
@@ -5,159 +5,9 @@ #ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_H_ #define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_NETWORK_PROVIDER_H_ -#include <stdint.h> - -#include <memory> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/supports_user_data.h" -#include "content/common/content_export.h" -#include "content/renderer/service_worker/service_worker_provider_context.h" -#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h" -#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h" -#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" - -namespace blink { -class WebServiceWorkerNetworkProvider; -} // namespace blink - -namespace network { -class SharedURLLoaderFactory; -} // namespace network - namespace content { -namespace mojom { -class URLLoaderFactory; -} - -struct CommitNavigationParams; -class RenderFrameImpl; -class ServiceWorkerProviderContext; - -// ServiceWorkerNetworkProvider enables the browser process to recognize -// resource requests from Blink that should be handled by service worker -// machinery rather than the usual network stack. -// -// All requests associated with a network provider are tagged with its -// |provider_id| (from ServiceWorkerProviderContext). The browser -// process can then route the request to this provider's corresponding -// ServiceWorkerProviderHost. -// -// It is created for service worker clients (documents and shared workers). It -// is instantiated prior to the main resource load being started and remains -// allocated until after the last subresource load has occurred. It is owned by -// the appropriate DocumentLoader for the provider (i.e., the loader for a -// document, or the shadow page's loader for a shared worker). -// Each request coming from the DocumentLoader is tagged with the provider_id in -// WillSendRequest. -class CONTENT_EXPORT ServiceWorkerNetworkProvider { - public: - // Creates a ServiceWorkerNetworkProvider for navigation and wraps it - // with WebServiceWorkerNetworkProvider to be owned by Blink. - // - // |commit_params| are navigation parameters that were transmitted to the - // renderer by the browser on a navigation commit. It is null if we have not - // yet heard from the browser (currently only during the time it takes from - // having the renderer initiate a navigation until the browser commits it). - // Note: in particular, provisional load failure do not provide - // |commit_params|. - // TODO(ahemery): Update this comment when do not create placeholder document - // loaders for renderer-initiated navigations. In this case, this should never - // be null. - // - // For S13nServiceWorker: - // |controller_info| contains the endpoint and object info that is needed to - // set up the controller service worker for the client. - // |fallback_loader_factory| is a default loader factory for fallback - // requests, and is used when we create a subresource loader for controllees. - // This is non-null only if the provider is created for controllees, and if - // the loading context, e.g. a frame, provides it. - static std::unique_ptr<blink::WebServiceWorkerNetworkProvider> - CreateForNavigation( - RenderFrameImpl* frame, - const CommitNavigationParams* commit_params, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory); - - // Creates a "null" network provider for a frame (it has an invalid provider - // id). Useful when the frame should not use service worker. - static std::unique_ptr<blink::WebServiceWorkerNetworkProvider> - CreateInvalidInstanceForNavigation(); - - // Creates a ServiceWorkerNetworkProvider for a shared worker (as a - // non-document service worker client). - // - // For NetworkService (PlzWorker): - // |controller_info| contains the endpoint and object info that is needed to - // set up the controller service worker for the client. - static std::unique_ptr<ServiceWorkerNetworkProvider> CreateForSharedWorker( - blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_info, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory); - - static ServiceWorkerNetworkProvider* FromWebServiceWorkerNetworkProvider( - blink::WebServiceWorkerNetworkProvider*); - - ~ServiceWorkerNetworkProvider(); - - int provider_id() const; - ServiceWorkerProviderContext* context() const { return context_.get(); } - - network::mojom::URLLoaderFactory* script_loader_factory() { - return script_loader_factory_.get(); - } - - // Returns whether the context this provider is for is controlled by a service - // worker. - blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker() const; - - // Called when blink::IdlenessDetector emits its network idle signal. - void DispatchNetworkQuiet(); - - private: - // Creates an invalid instance (provider_id() returns - // kInvalidServiceWorkerProviderId). - ServiceWorkerNetworkProvider(); - - // |is_parent_frame_secure| is only relevant when the - // |type| is kForWindow. - // - // For S13nServiceWorker: - // See the comment at CreateForNavigation() for |controller_info| and - // |fallback_loader_factory|. - ServiceWorkerNetworkProvider( - int route_id, - blink::mojom::ServiceWorkerProviderType type, - int provider_id, - bool is_parent_frame_secure, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory); - - ServiceWorkerNetworkProvider( - blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, - network::mojom::URLLoaderFactoryAssociatedPtrInfo - script_loader_factory_info, - blink::mojom::ControllerServiceWorkerInfoPtr controller_info, - scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory); - - // |context_| is null if |this| is an invalid instance, in which case there is - // no connection to the browser process. - scoped_refptr<ServiceWorkerProviderContext> context_; - - blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_; - - // For shared worker contexts. The URL loader factory for loading the worker's - // scripts. - network::mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_; - - DISALLOW_COPY_AND_ASSIGN(ServiceWorkerNetworkProvider); -}; +int GetNextServiceWorkerProviderId(); } // namespace content
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h index 57e6bc1..a8ed9398 100644 --- a/content/renderer/service_worker/service_worker_provider_context.h +++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -136,9 +136,9 @@ // host. blink::mojom::ServiceWorkerContainerHostPtrInfo CloneContainerHostPtrInfo(); - // Called when ServiceWorkerNetworkProvider is destructed. This function + // Called when WebServiceWorkerNetworkProvider is destructed. This function // severs the Mojo binding to the browser-side ServiceWorkerProviderHost. The - // reason ServiceWorkerNetworkProvider is special compared to the other + // reason WebServiceWorkerNetworkProvider is special compared to the other // providers, is that it is destructed synchronously when a service worker // client (Document) is removed from the DOM. Once this happens, the // ServiceWorkerProviderHost must destruct quickly in order to remove the
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_base_impl.cc b/content/renderer/service_worker/web_service_worker_network_provider_base_impl.cc deleted file mode 100644 index ac19ee5f..0000000 --- a/content/renderer/service_worker/web_service_worker_network_provider_base_impl.cc +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h" - -#include "content/renderer/service_worker/service_worker_network_provider.h" - -namespace content { - -WebServiceWorkerNetworkProviderBaseImpl:: - WebServiceWorkerNetworkProviderBaseImpl( - std::unique_ptr<ServiceWorkerNetworkProvider> provider) - : provider_(std::move(provider)) {} - -WebServiceWorkerNetworkProviderBaseImpl:: - ~WebServiceWorkerNetworkProviderBaseImpl() = default; - -blink::mojom::ControllerServiceWorkerMode -WebServiceWorkerNetworkProviderBaseImpl::IsControlledByServiceWorker() { - return provider_->IsControlledByServiceWorker(); -} - -int64_t WebServiceWorkerNetworkProviderBaseImpl::ControllerServiceWorkerID() { - if (provider_->context()) - return provider_->context()->GetControllerVersionId(); - return blink::mojom::kInvalidServiceWorkerVersionId; -} - -} // namespace content
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_base_impl.h b/content/renderer/service_worker/web_service_worker_network_provider_base_impl.h deleted file mode 100644 index df10352a..0000000 --- a/content/renderer/service_worker/web_service_worker_network_provider_base_impl.h +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_BASE_IMPL_H_ -#define CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_BASE_IMPL_H_ - -#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" - -namespace content { - -class ServiceWorkerNetworkProvider; - -// This is the base implementation of WebServiceWorkerNetworkProvider. All -// functions are called on the main thread only. -class WebServiceWorkerNetworkProviderBaseImpl - : public blink::WebServiceWorkerNetworkProvider { - public: - explicit WebServiceWorkerNetworkProviderBaseImpl( - std::unique_ptr<ServiceWorkerNetworkProvider>); - ~WebServiceWorkerNetworkProviderBaseImpl() override; - - // Implement blink::WebServiceWorkerNetworkProvider. - blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker() final; - int64_t ControllerServiceWorkerID() final; - - ServiceWorkerNetworkProvider* provider() { return provider_.get(); } - - private: - std::unique_ptr<ServiceWorkerNetworkProvider> provider_; -}; - -} // namespace content - -#endif // CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_BASE_IMPL_H_
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc index f86037f..89c1a888 100644 --- a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc +++ b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc
@@ -6,6 +6,8 @@ #include <utility> +#include "content/common/navigation_params.h" +#include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/origin_util.h" #include "content/public/renderer/render_frame_observer.h" #include "content/renderer/loader/request_extra_data.h" @@ -18,6 +20,21 @@ namespace content { +namespace { +// Returns whether it's possible for a document whose frame is a descendant of +// |frame| to be a secure context, not considering scheme exceptions (since any +// document can be a secure context if it has a scheme exception). See +// Document::isSecureContextImpl for more details. +bool IsFrameSecure(blink::WebFrame* frame) { + while (frame) { + if (!frame->GetSecurityOrigin().IsPotentiallyTrustworthy()) + return false; + frame = frame->Parent(); + } + return true; +} +} // namespace + class WebServiceWorkerNetworkProviderImplForFrame::NewDocumentObserver : public RenderFrameObserver { public: @@ -36,7 +53,7 @@ // service workers so created the network provider, but it turns out it is // not eligible because it is CSP sandboxed. web_loader->SetServiceWorkerNetworkProvider( - ServiceWorkerNetworkProvider::CreateInvalidInstanceForNavigation()); + WebServiceWorkerNetworkProviderImplForFrame::CreateInvalidInstance()); // |this| and its owner are destroyed. return; } @@ -53,24 +70,114 @@ WebServiceWorkerNetworkProviderImplForFrame* owner_; }; +// static +std::unique_ptr<WebServiceWorkerNetworkProviderImplForFrame> +WebServiceWorkerNetworkProviderImplForFrame::Create( + RenderFrameImpl* frame, + const CommitNavigationParams* commit_params, + blink::mojom::ControllerServiceWorkerInfoPtr controller_info, + scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) { + blink::WebLocalFrame* web_frame = frame->GetWebFrame(); + // Determine if a provider should be created and properly initialized for the + // navigation. A default provider will always be created since it is expected + // in a certain number of places, however it will have an invalid id. + bool should_create_provider = false; + int provider_id = kInvalidServiceWorkerProviderId; + if (commit_params) { + should_create_provider = commit_params->should_create_service_worker; + provider_id = commit_params->service_worker_provider_id; + } else { + // It'd be convenient to check web_frame->GetSecurityOrigin().IsOpaque() + // here instead of just looking at the sandbox flags, but + // GetSecurityOrigin() crashes because the frame does not yet have a + // security context. + should_create_provider = + ((web_frame->EffectiveSandboxFlags() & + blink::WebSandboxFlags::kOrigin) != blink::WebSandboxFlags::kOrigin); + } + + // If we shouldn't create a real provider, return one with an invalid id. + if (!should_create_provider) { + return CreateInvalidInstance(); + } + + // Otherwise, create the provider. + + // Ideally Document::IsSecureContext would be called here, but the document is + // not created yet, and due to redirects the URL may change. So pass + // is_parent_frame_secure to the browser process, so it can determine the + // context security when deciding whether to allow a service worker to control + // the document. + const bool is_parent_frame_secure = IsFrameSecure(web_frame->Parent()); + + // If the browser process did not assign a provider id already, assign one + // now (see class comments for content::ServiceWorkerProviderHost). + DCHECK(ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id) || + provider_id == kInvalidServiceWorkerProviderId); + if (provider_id == kInvalidServiceWorkerProviderId) + provider_id = GetNextServiceWorkerProviderId(); + + auto provider = + base::WrapUnique(new WebServiceWorkerNetworkProviderImplForFrame(frame)); + + auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New( + provider_id, frame->GetRoutingID(), + blink::mojom::ServiceWorkerProviderType::kForWindow, + is_parent_frame_secure, nullptr /* host_request */, + nullptr /* client_ptr_info */); + blink::mojom::ServiceWorkerContainerAssociatedRequest client_request = + mojo::MakeRequest(&host_info->client_ptr_info); + blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; + host_info->host_request = mojo::MakeRequest(&host_ptr_info); + + provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>( + provider_id, blink::mojom::ServiceWorkerProviderType::kForWindow, + std::move(client_request), std::move(host_ptr_info), + std::move(controller_info), std::move(fallback_loader_factory)); + + if (ChildThreadImpl::current()) { + ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( + &provider->dispatcher_host_); + provider->dispatcher_host_->OnProviderCreated(std::move(host_info)); + } else { + // current() may be null in tests. Silently drop messages sent over + // ServiceWorkerContainerHost since we couldn't set it up correctly due to + // this test limitation. This way we don't crash when the associated + // interface ptr is used. + // + // TODO(falken): Just give ServiceWorkerProviderContext a null interface ptr + // and make the callsites deal with it. They are supposed to anyway because + // OnNetworkProviderDestroyed() can reset the ptr to null at any time. + mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle()); + } + return provider; +} + +// static +std::unique_ptr<WebServiceWorkerNetworkProviderImplForFrame> +WebServiceWorkerNetworkProviderImplForFrame::CreateInvalidInstance() { + return base::WrapUnique( + new WebServiceWorkerNetworkProviderImplForFrame(nullptr)); +} + WebServiceWorkerNetworkProviderImplForFrame:: - WebServiceWorkerNetworkProviderImplForFrame( - std::unique_ptr<ServiceWorkerNetworkProvider> provider, - RenderFrameImpl* frame) - : WebServiceWorkerNetworkProviderBaseImpl(std::move(provider)) { + WebServiceWorkerNetworkProviderImplForFrame(RenderFrameImpl* frame) { if (frame) observer_ = std::make_unique<NewDocumentObserver>(this, frame); } WebServiceWorkerNetworkProviderImplForFrame:: - ~WebServiceWorkerNetworkProviderImplForFrame() = default; + ~WebServiceWorkerNetworkProviderImplForFrame() { + if (context()) + context()->OnNetworkProviderDestroyed(); +} void WebServiceWorkerNetworkProviderImplForFrame::WillSendRequest( blink::WebURLRequest& request) { if (!request.GetExtraData()) request.SetExtraData(std::make_unique<RequestExtraData>()); auto* extra_data = static_cast<RequestExtraData*>(request.GetExtraData()); - extra_data->set_service_worker_provider_id(provider()->provider_id()); + extra_data->set_service_worker_provider_id(provider_id()); // If the provider does not have a controller at this point, the renderer // expects the request to never be handled by a service worker, so call @@ -92,8 +199,22 @@ // requests or non-S13nSW case, the browser process sets the id on the // request when dispatching the fetch event to the service worker. But it // doesn't hurt to set it always. - if (provider()->context()) - request.SetFetchWindowId(provider()->context()->fetch_request_window_id()); + if (context()) + request.SetFetchWindowId(context()->fetch_request_window_id()); +} + +blink::mojom::ControllerServiceWorkerMode +WebServiceWorkerNetworkProviderImplForFrame::IsControlledByServiceWorker() { + if (!context()) + return blink::mojom::ControllerServiceWorkerMode::kNoController; + return context()->IsControlledByServiceWorker(); +} + +int64_t +WebServiceWorkerNetworkProviderImplForFrame::ControllerServiceWorkerID() { + if (!context()) + return blink::mojom::kInvalidServiceWorkerVersionId; + return context()->GetControllerVersionId(); } std::unique_ptr<blink::WebURLLoader> @@ -112,8 +233,7 @@ // We need SubresourceLoaderFactory populated in order to create our own // URLLoader for subresource loading. - if (!provider()->context() || - !provider()->context()->GetSubresourceLoaderFactory()) + if (!context() || !context()->GetSubresourceLoaderFactory()) return nullptr; // If the URL is not http(s) or otherwise whitelisted, do not intercept the @@ -138,16 +258,24 @@ RenderThreadImpl::current()->resource_dispatcher(), std::move(task_runner_handle), base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - provider()->context()->GetSubresourceLoaderFactory())); + context()->GetSubresourceLoaderFactory())); } void WebServiceWorkerNetworkProviderImplForFrame::DispatchNetworkQuiet() { - provider()->DispatchNetworkQuiet(); + if (!context()) + return; + context()->DispatchNetworkQuiet(); +} + +int WebServiceWorkerNetworkProviderImplForFrame::provider_id() const { + if (!context_) + return kInvalidServiceWorkerProviderId; + return context_->provider_id(); } void WebServiceWorkerNetworkProviderImplForFrame::NotifyExecutionReady() { - if (provider()->context()) - provider()->context()->NotifyExecutionReady(); + if (context()) + context()->NotifyExecutionReady(); } } // namespace content
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h index 9baad27..a742ee10 100644 --- a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h +++ b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h
@@ -7,36 +7,80 @@ #include <memory> -#include "content/renderer/service_worker/service_worker_network_provider.h" -#include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h" +#include "content/common/content_export.h" +#include "content/renderer/service_worker/service_worker_provider_context.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h" +#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" namespace content { - +struct CommitNavigationParams; class RenderFrameImpl; -// An WebServiceWorkerNetworkProvider for frame. This wraps -// ServiceWorkerNetworkProvider implementation and is owned by blink. -class WebServiceWorkerNetworkProviderImplForFrame final - : public WebServiceWorkerNetworkProviderBaseImpl { +// The WebServiceWorkerNetworkProvider implementation used for frames. +class CONTENT_EXPORT WebServiceWorkerNetworkProviderImplForFrame final + : public blink::WebServiceWorkerNetworkProvider { public: - WebServiceWorkerNetworkProviderImplForFrame( - std::unique_ptr<ServiceWorkerNetworkProvider> provider, - RenderFrameImpl* frame); + // Creates a network provider for |frame|. + // + // |commit_params| are navigation parameters that were transmitted to the + // renderer by the browser on a navigation commit. It is null if we have not + // yet heard from the browser (currently only during the time it takes from + // having the renderer initiate a navigation until the browser commits it). + // Note: in particular, provisional load failure do not provide + // |commit_params|. + // TODO(ahemery): Update this comment when do not create placeholder document + // loaders for renderer-initiated navigations. In this case, this should never + // be null. + // + // For S13nServiceWorker: + // |controller_info| contains the endpoint and object info that is needed to + // set up the controller service worker for the client. + // |fallback_loader_factory| is a default loader factory for fallback + // requests, and is used when we create a subresource loader for controllees. + // This is non-null only if the provider is created for controllees, and if + // the loading context, e.g. a frame, provides it. + static std::unique_ptr<WebServiceWorkerNetworkProviderImplForFrame> Create( + RenderFrameImpl* frame, + const CommitNavigationParams* commit_params, + blink::mojom::ControllerServiceWorkerInfoPtr controller_info, + scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory); + + // Creates an invalid instance. It has a null |context()|. + // TODO(falken): Just use null instead of this. + static std::unique_ptr<WebServiceWorkerNetworkProviderImplForFrame> + CreateInvalidInstance(); + ~WebServiceWorkerNetworkProviderImplForFrame() override; - // Implements WebServiceWorkerNetworkProviderBaseImpl. + // Implements WebServiceWorkerNetworkProvider. void WillSendRequest(blink::WebURLRequest& request) override; + blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker() + override; + int64_t ControllerServiceWorkerID() override; std::unique_ptr<blink::WebURLLoader> CreateURLLoader( const blink::WebURLRequest& request, std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle> task_runner_handle) override; void DispatchNetworkQuiet() override; + int provider_id() const; + ServiceWorkerProviderContext* context() { return context_.get(); } + private: class NewDocumentObserver; + explicit WebServiceWorkerNetworkProviderImplForFrame(RenderFrameImpl* frame); + void NotifyExecutionReady(); + // |context_| is null if |this| is an invalid instance, in which case there is + // no connection to the browser process. + scoped_refptr<ServiceWorkerProviderContext> context_; + + blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_; + std::unique_ptr<NewDocumentObserver> observer_; };
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc index 5481f706..479b262 100644 --- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc +++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -253,21 +253,18 @@ std::unique_ptr<blink::WebServiceWorkerNetworkProvider> EmbeddedSharedWorkerStub::CreateServiceWorkerNetworkProvider() { - std::unique_ptr<ServiceWorkerNetworkProvider> provider = - ServiceWorkerNetworkProvider::CreateForSharedWorker( - std::move(service_worker_provider_info_), - std::move(main_script_loader_factory_), std::move(controller_info_), - subresource_loader_factories_); - - return std::make_unique<WebServiceWorkerNetworkProviderImplForWorker>( - std::move(provider), IsOriginSecure(url_), std::move(response_override_)); + return WebServiceWorkerNetworkProviderImplForWorker::Create( + std::move(service_worker_provider_info_), + std::move(main_script_loader_factory_), std::move(controller_info_), + subresource_loader_factories_, IsOriginSecure(url_), + std::move(response_override_)); } void EmbeddedSharedWorkerStub::WaitForServiceWorkerControllerInfo( blink::WebServiceWorkerNetworkProvider* web_network_provider, base::OnceClosure callback) { ServiceWorkerProviderContext* context = - ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( + static_cast<WebServiceWorkerNetworkProviderImplForWorker*>( web_network_provider) ->context(); context->PingContainerHost(std::move(callback)); @@ -277,8 +274,8 @@ EmbeddedSharedWorkerStub::CreateWorkerFetchContext( blink::WebServiceWorkerNetworkProvider* web_network_provider) { DCHECK(web_network_provider); - ServiceWorkerNetworkProvider* network_provider = - ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( + WebServiceWorkerNetworkProviderImplForWorker* network_provider = + static_cast<WebServiceWorkerNetworkProviderImplForWorker*>( web_network_provider); // Make the factory used for service worker network fallback (that should @@ -288,7 +285,7 @@ scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context = WebWorkerFetchContextImpl::Create( - network_provider, std::move(renderer_preferences_), + network_provider->context(), std::move(renderer_preferences_), std::move(preference_watcher_request_), subresource_loader_factories_->Clone(), std::move(fallback_factory));
diff --git a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc index 91863a1b..9d183e1 100644 --- a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc +++ b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc
@@ -4,6 +4,8 @@ #include "content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h" +#include <utility> + #include "base/feature_list.h" #include "content/public/common/origin_util.h" #include "content/renderer/loader/navigation_response_override_parameters.h" @@ -17,22 +19,76 @@ namespace content { -WebServiceWorkerNetworkProviderImplForWorker:: - WebServiceWorkerNetworkProviderImplForWorker( - std::unique_ptr<ServiceWorkerNetworkProvider> provider, - bool is_secure_context, - std::unique_ptr<NavigationResponseOverrideParameters> response_override) - : WebServiceWorkerNetworkProviderBaseImpl(std::move(provider)), - is_secure_context_(is_secure_context), - response_override_(std::move(response_override)) {} +std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker> +WebServiceWorkerNetworkProviderImplForWorker::Create( + blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, + network::mojom::URLLoaderFactoryAssociatedPtrInfo + script_loader_factory_info, + blink::mojom::ControllerServiceWorkerInfoPtr controller_info, + scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory, + bool is_secure_context, + std::unique_ptr<NavigationResponseOverrideParameters> response_override) { + auto provider = + base::WrapUnique(new WebServiceWorkerNetworkProviderImplForWorker( + is_secure_context, std::move(response_override))); + if (blink::ServiceWorkerUtils::IsServicificationEnabled()) { + DCHECK(info); + provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>( + info->provider_id, + blink::mojom::ServiceWorkerProviderType::kForSharedWorker, + std::move(info->client_request), std::move(info->host_ptr_info), + std::move(controller_info), std::move(fallback_loader_factory)); + if (script_loader_factory_info.is_valid()) { + provider->script_loader_factory_.Bind( + std::move(script_loader_factory_info)); + } + } else { + DCHECK(!info); + int provider_id = GetNextServiceWorkerProviderId(); + auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New( + provider_id, MSG_ROUTING_NONE, + blink::mojom::ServiceWorkerProviderType::kForSharedWorker, + true /* is_parent_frame_secure */, nullptr /* host_request */, + nullptr /* client_ptr_info */); + blink::mojom::ServiceWorkerContainerAssociatedRequest client_request = + mojo::MakeRequest(&host_info->client_ptr_info); + blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; + host_info->host_request = mojo::MakeRequest(&host_ptr_info); + + provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>( + provider_id, blink::mojom::ServiceWorkerProviderType::kForSharedWorker, + std::move(client_request), std::move(host_ptr_info), + std::move(controller_info), std::move(fallback_loader_factory)); + if (ChildThreadImpl::current()) { + ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( + &provider->dispatcher_host_); + provider->dispatcher_host_->OnProviderCreated(std::move(host_info)); + } else { + // current() may be null in tests. Silently drop messages sent over + // ServiceWorkerContainerHost since we couldn't set it up correctly due + // to this test limitation. This way we don't crash when the associated + // interface ptr is used. + // + // TODO(falken): Just give ServiceWorkerProviderContext a null interface + // ptr and make the callsites deal with it. They are supposed to anyway + // because OnNetworkProviderDestroyed() can reset the ptr to null at any + // time. + mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle()); + } + } + return provider; +} WebServiceWorkerNetworkProviderImplForWorker:: - ~WebServiceWorkerNetworkProviderImplForWorker() = default; + ~WebServiceWorkerNetworkProviderImplForWorker() { + if (context()) + context()->OnNetworkProviderDestroyed(); +} void WebServiceWorkerNetworkProviderImplForWorker::WillSendRequest( blink::WebURLRequest& request) { auto extra_data = std::make_unique<RequestExtraData>(); - extra_data->set_service_worker_provider_id(provider()->provider_id()); + extra_data->set_service_worker_provider_id(provider_id()); extra_data->set_initiated_in_secure_context(is_secure_context_); if (response_override_) { DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); @@ -50,12 +106,26 @@ // request and break the assumptions of the renderer. if (request.GetRequestContext() != blink::mojom::RequestContextType::SHARED_WORKER && - provider()->IsControlledByServiceWorker() == + IsControlledByServiceWorker() == blink::mojom::ControllerServiceWorkerMode::kNoController) { request.SetSkipServiceWorker(true); } } +blink::mojom::ControllerServiceWorkerMode +WebServiceWorkerNetworkProviderImplForWorker::IsControlledByServiceWorker() { + if (!context()) + return blink::mojom::ControllerServiceWorkerMode::kNoController; + return context()->IsControlledByServiceWorker(); +} + +int64_t +WebServiceWorkerNetworkProviderImplForWorker::ControllerServiceWorkerID() { + if (!context()) + return blink::mojom::kInvalidServiceWorkerVersionId; + return context()->GetControllerVersionId(); +} + std::unique_ptr<blink::WebURLLoader> WebServiceWorkerNetworkProviderImplForWorker::CreateURLLoader( const blink::WebURLRequest& request, @@ -72,7 +142,7 @@ return nullptr; } // If the request is for the main script, use the script_loader_factory. - if (provider()->script_loader_factory() && + if (script_loader_factory_ && request.GetRequestContext() == blink::mojom::RequestContextType::SHARED_WORKER) { // TODO(crbug.com/796425): Temporarily wrap the raw @@ -80,13 +150,12 @@ return std::make_unique<WebURLLoaderImpl>( render_thread->resource_dispatcher(), std::move(task_runner_handle), base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - provider()->script_loader_factory())); + script_loader_factory_.get())); } // Otherwise, it's an importScript. Use the subresource loader factory if it // exists (we are controlled by a service worker). - if (!provider()->context() || - !provider()->context()->GetSubresourceLoaderFactory()) { + if (!context() || !context()->GetSubresourceLoaderFactory()) { return nullptr; } @@ -113,7 +182,20 @@ RenderThreadImpl::current()->resource_dispatcher(), std::move(task_runner_handle), base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - provider()->context()->GetSubresourceLoaderFactory())); + context()->GetSubresourceLoaderFactory())); } +int WebServiceWorkerNetworkProviderImplForWorker::provider_id() const { + if (!context_) + return kInvalidServiceWorkerProviderId; + return context_->provider_id(); +} + +WebServiceWorkerNetworkProviderImplForWorker:: + WebServiceWorkerNetworkProviderImplForWorker( + bool is_secure_context, + std::unique_ptr<NavigationResponseOverrideParameters> response_override) + : is_secure_context_(is_secure_context), + response_override_(std::move(response_override)) {} + } // namespace content
diff --git a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h index 580b1777..1c31ae2 100644 --- a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h +++ b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h
@@ -5,37 +5,77 @@ #ifndef CONTENT_RENDERER_SHARED_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_ #define CONTENT_RENDERER_SHARED_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_ -#include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h" +#include <memory> + +#include "base/memory/ref_counted.h" +#include "content/renderer/service_worker/service_worker_provider_context.h" +#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" +#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" namespace content { -class ServiceWorkerNetworkProvider; struct NavigationResponseOverrideParameters; -// This is the implementation of WebServiceWorkerNetworkProvider used for -// shared workers and owned by Blink. All functions are called on the main -// thread only. +// The WebServiceWorkerNetworkProvider implementation used for shared +// workers. class WebServiceWorkerNetworkProviderImplForWorker final - : public WebServiceWorkerNetworkProviderBaseImpl { + : public blink::WebServiceWorkerNetworkProvider { public: + // Creates a new instance. Some params might only be used in S13nServiceWorker + // or PlzSharedWorker. + // - |info|: provider info from the browser + // - |script_loader_factory_info|: the factory for loading the worker's + // scripts + // - |controller_info|: info about controller service worker + // - |fallback_loader_factory|: the factory to use when a service worker falls + // back to network (unlike the default factory of this renderer, it skips + // AppCache) + // - |is_secure_context|: whether this context is secure + // - |response_override|: the main script response + static std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker> Create( + blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info, + network::mojom::URLLoaderFactoryAssociatedPtrInfo + script_loader_factory_info, + blink::mojom::ControllerServiceWorkerInfoPtr controller_info, + scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory, + bool is_secure_context, + std::unique_ptr<NavigationResponseOverrideParameters> response_override); + WebServiceWorkerNetworkProviderImplForWorker( - std::unique_ptr<ServiceWorkerNetworkProvider> provider, bool is_secure_context, std::unique_ptr<NavigationResponseOverrideParameters> response_override); ~WebServiceWorkerNetworkProviderImplForWorker() override; + // Implements WebServiceWorkerNetworkProvider. // Blink calls this method for each request starting with the main script, // we tag them with the provider id. void WillSendRequest(blink::WebURLRequest& request) override; - + blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker() + override; + int64_t ControllerServiceWorkerID() override; std::unique_ptr<blink::WebURLLoader> CreateURLLoader( const blink::WebURLRequest& request, std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle> task_runner_handle) override; + int provider_id() const; + ServiceWorkerProviderContext* context() { return context_.get(); } + private: const bool is_secure_context_; std::unique_ptr<NavigationResponseOverrideParameters> response_override_; + + // |context_| is null if |this| is an invalid instance, in which case there is + // no connection to the browser process. + scoped_refptr<ServiceWorkerProviderContext> context_; + + // Used in non-s13nsw. + blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_; + + // The URL loader factory for loading the worker's scripts. + network::mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_; }; } // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 139a6d5..0d5edc2 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1595,6 +1595,7 @@ "../browser/renderer_host/media/video_capture_controller_unittest.cc", "../browser/renderer_host/media/video_capture_manager_unittest.cc", "../browser/renderer_host/media/video_capture_unittest.cc", + "../browser/renderer_host/native_web_keyboard_event_mac_unittest.mm", "../browser/renderer_host/overscroll_controller_unittest.cc", "../browser/renderer_host/render_process_host_unittest.cc", "../browser/renderer_host/render_view_host_unittest.cc", @@ -2094,7 +2095,10 @@ "//third_party/ocmock", "//ui/accelerated_widget_mac", ] - libs = [ "IOSurface.framework" ] + libs = [ + "Carbon.framework", + "IOSurface.framework", + ] } if (is_chromeos) { deps += [ "//chromeos/audio" ]
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py index 49a1a0c9..8a61b577 100644 --- a/content/test/gpu/gpu_tests/pixel_expectations.py +++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -49,16 +49,11 @@ # TODO(vmiura) check / generate reference images for Android devices self.Fail('Pixel_SolidColorBackground', ['mac', 'android'], bug=624256) - # TODO(wangxianzhu): This is commented out temporarily because of the entry - # for crbug.com/836884. - # self.Fail('Pixel_CSSFilterEffects', ['mac', ('nvidia', 0xfe9)], - # bug=690277) + self.Fail('Pixel_CSSFilterEffects', ['mac', ('nvidia', 0xfe9)], bug=690277) # Became flaky on 10.13.6. When it flakes, it flakes 3 times, so # mark failing, unfortunately. - # TODO(wangxianzhu): This is commented out temporarily because of the entry - # for crbug.com/836884. - # self.Fail('Pixel_CSSFilterEffects', ['highsierra', 'amd'], bug=872423) + self.Fail('Pixel_CSSFilterEffects', ['highsierra', 'amd'], bug=872423) # TODO(kbr): flakily timing out on this configuration. self.Flaky('*', ['linux', 'intel', 'debug'], bug=648369) @@ -121,12 +116,6 @@ self.Flaky('Pixel_BackgroundImage', ['android', ('qualcomm', 'Adreno (TM) 418')], bug=883500) - # TODO(wangxianzhu): Re-enable after and rebaselining - self.Fail('Pixel_CSSFilterEffects', bug=836884) - self.Fail('Pixel_CSSFilterEffects_NoOverlays', bug=836884) - self.Fail('Pixel_2DCanvasWebGL', bug=836884) - self.Fail('Pixel_CSS3DBlueBox', bug=836884) - # We do not have software H.264 decoding on Android, so it can't survive a # context loss which results in hardware decoder loss. self.Skip('Pixel_Video_Context_Loss_MP4', ['android'], bug=580386) @@ -145,6 +134,9 @@ self.Fail('Pixel_Video_MP4_FourColors_Rot_270', ['mac', ('amd', 0x679e)], bug=911413) + # Fails on multiple Android devices. + self.Fail('Pixel_CSS3DBlueBox', ['android'], bug=927107) + # Fail on Nexus 5, 5X, 6, 6P, 9 and Shield TV. self.Fail('Pixel_Video_MP4', ['android'], bug=925744) self.Fail('Pixel_Video_MP4_FourColors_Aspect_4x3', ['android'], bug=925744)
diff --git a/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc b/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc index d40f1ce2..a53422c2d 100644 --- a/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc +++ b/device/bluetooth/bluetooth_gatt_discoverer_winrt.cc
@@ -18,6 +18,8 @@ namespace { +using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice; +using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattCharacteristic; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: @@ -25,6 +27,8 @@ using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattCommunicationStatus; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattCommunicationStatus_AccessDenied; +using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: GattCommunicationStatus_Success; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: @@ -43,15 +47,14 @@ IGattDeviceService3; using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile:: IGattDeviceServicesResult; -using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3; -using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice; -using ABI::Windows::Foundation::Collections::IVectorView; using ABI::Windows::Foundation::IAsyncOperation; using ABI::Windows::Foundation::IReference; +using ABI::Windows::Foundation::Collections::IVectorView; using Microsoft::WRL::ComPtr; template <typename IGattResult> -bool CheckCommunicationStatus(IGattResult* gatt_result) { +bool CheckCommunicationStatus(IGattResult* gatt_result, + bool allow_access_denied = false) { if (!gatt_result) { VLOG(2) << "Getting GATT Results failed."; return false; @@ -66,14 +69,20 @@ } if (status != GattCommunicationStatus_Success) { - VLOG(2) << "Unexpected GattCommunicationStatus: " << status; + if (status == GattCommunicationStatus_AccessDenied) { + VLOG(2) << "GATT access denied error"; + } else { + VLOG(2) << "Unexpected GattCommunicationStatus: " << status; + } VLOG(2) << "GATT Error Code: " << static_cast<int>( BluetoothRemoteGattServiceWinrt::GetGattErrorCode( gatt_result)); } - return status == GattCommunicationStatus_Success; + return status == GattCommunicationStatus_Success || + (allow_access_denied && + status == GattCommunicationStatus_AccessDenied); } template <typename T, typename I> @@ -233,7 +242,9 @@ void BluetoothGattDiscovererWinrt::OnGetCharacteristics( uint16_t service_attribute_handle, ComPtr<IGattCharacteristicsResult> characteristics_result) { - if (!CheckCommunicationStatus(characteristics_result.Get())) { + // A few GATT services like HID over GATT (short UUID 0x1812) are protected + // by the OS, leading to an access denied error. + if (!CheckCommunicationStatus(characteristics_result.Get(), true)) { std::move(callback_).Run(false); return; }
diff --git a/device/usb/mojo/device_manager_impl.cc b/device/usb/mojo/device_manager_impl.cc index 9018ea2..0ddbe4b 100644 --- a/device/usb/mojo/device_manager_impl.cc +++ b/device/usb/mojo/device_manager_impl.cc
@@ -18,6 +18,8 @@ #include "device/usb/mojo/type_converters.h" #include "device/usb/public/cpp/usb_utils.h" #include "device/usb/public/mojom/device.mojom.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" +#include "device/usb/public/mojom/device_manager_client.mojom.h" #include "device/usb/usb_device.h" #include "device/usb/usb_service.h"
diff --git a/device/usb/mojo/device_manager_impl_unittest.cc b/device/usb/mojo/device_manager_impl_unittest.cc index 5dafdc39..707b610 100644 --- a/device/usb/mojo/device_manager_impl_unittest.cc +++ b/device/usb/mojo/device_manager_impl_unittest.cc
@@ -22,6 +22,8 @@ #include "device/usb/mock_usb_device_handle.h" #include "device/usb/mock_usb_service.h" #include "device/usb/mojo/device_impl.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" +#include "device/usb/public/mojom/device_manager_client.mojom.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/device/usb/public/cpp/fake_usb_device_manager.cc b/device/usb/public/cpp/fake_usb_device_manager.cc index ee9bc1a..b88b504 100644 --- a/device/usb/public/cpp/fake_usb_device_manager.cc +++ b/device/usb/public/cpp/fake_usb_device_manager.cc
@@ -12,6 +12,8 @@ #include "base/threading/thread_task_runner_handle.h" #include "device/usb/public/cpp/fake_usb_device.h" #include "device/usb/public/cpp/usb_utils.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" +#include "device/usb/public/mojom/device_manager_client.mojom.h" namespace device {
diff --git a/device/usb/public/cpp/usb_utils.cc b/device/usb/public/cpp/usb_utils.cc index 5b1f1839..4877c53 100644 --- a/device/usb/public/cpp/usb_utils.cc +++ b/device/usb/public/cpp/usb_utils.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" #include "device/usb/usb_device.h" namespace device {
diff --git a/device/usb/public/cpp/usb_utils_unittest.cc b/device/usb/public/cpp/usb_utils_unittest.cc index 426a097..9fc5ada 100644 --- a/device/usb/public/cpp/usb_utils_unittest.cc +++ b/device/usb/public/cpp/usb_utils_unittest.cc
@@ -8,6 +8,7 @@ #include "base/strings/utf_string_conversions.h" #include "device/usb/mock_usb_device.h" #include "device/usb/public/cpp/usb_utils.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" #include "device/usb/usb_descriptors.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/docs/speed/microbenchmark_regressions.md b/docs/speed/microbenchmark_regressions.md index 26ba377..f2f7f89e 100644 --- a/docs/speed/microbenchmark_regressions.md +++ b/docs/speed/microbenchmark_regressions.md
@@ -25,6 +25,9 @@ to dig into this is to [examine the compiled functions](../disassemble_code.md) before and after the no-op change, to see if inlining has changed. +If you have a function that you believe should always be inlined in production +builds, please contact gbiv@chromium.org for advice. + ### Toolchain rolls Our toolchain team regularly rolls in new versions of clang, the compiler for
diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc index ec82c1a2..ff1da54 100644 --- a/extensions/browser/api/device_permissions_prompt.cc +++ b/extensions/browser/api/device_permissions_prompt.cc
@@ -17,6 +17,10 @@ #include "content/public/browser/browser_thread.h" #include "content/public/common/service_manager_connection.h" #include "device/usb/public/cpp/usb_utils.h" +#include "device/usb/public/mojom/device_enumeration_options.mojom.h" +#include "device/usb/usb_device.h" +#include "device/usb/usb_ids.h" +#include "device/usb/usb_service.h" #include "extensions/browser/api/device_permissions_manager.h" #include "extensions/browser/api/usb/usb_device_manager.h" #include "extensions/common/extension.h"
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc index ac5e02a..8dc63f8 100644 --- a/google_apis/gcm/engine/connection_factory_impl.cc +++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -25,6 +25,7 @@ #include "net/socket/client_socket_pool_manager.h" #include "net/ssl/ssl_config_service.h" #include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/mojom/tcp_socket.mojom.h" namespace gcm {
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h index 4cd9cb8b..2861938 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
@@ -147,6 +147,10 @@ void SetResizesScrollView(bool resizes_scroll_view); bool ResizesScrollView() const; + // Setter for the safe area insets for the current WebState's view. + void SetWebViewSafeAreaInsets(UIEdgeInsets safe_area_insets); + UIEdgeInsets GetWebViewSafeAreaInsets() const; + private: // Returns how a scroll to the current |y_content_offset_| from |from_offset| // should be handled. @@ -220,6 +224,8 @@ bool ignoring_current_scroll_ = false; // Whether the scroll view is resized for fullscreen events. bool resizes_scroll_view_ = false; + // The WebState view's safe area insets. + UIEdgeInsets safe_area_insets_ = UIEdgeInsetsZero; // The number of FullscreenModelObserver callbacks currently being executed. size_t observer_callback_count_ = 0;
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm index daa1aadb..b639cb6 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
@@ -228,6 +228,17 @@ return resizes_scroll_view_; } +void FullscreenModel::SetWebViewSafeAreaInsets(UIEdgeInsets safe_area_insets) { + if (UIEdgeInsetsEqualToEdgeInsets(safe_area_insets_, safe_area_insets)) + return; + safe_area_insets_ = safe_area_insets; + UpdateDisabledCounterForContentHeight(); +} + +UIEdgeInsets FullscreenModel::GetWebViewSafeAreaInsets() const { + return safe_area_insets_; +} + FullscreenModel::ScrollAction FullscreenModel::ActionForScrollFromOffset( CGFloat from_offset) const { // Update the base offset but don't recalculate progress if: @@ -279,6 +290,14 @@ // resized to account for the viewport insets after the page has been // rendered, so account for the maximum toolbar insets in the threshold. disabling_threshold += expanded_toolbar_height_ + bottom_toolbar_height_; + } else { + // After reloads, pages whose viewports fit the screen are sometimes resized + // to account for the safe area insets. Adding these to the threshold helps + // prevent fullscreen from beeing re-enabled in this case. + // TODO(crbug.com/924807): This logic can potentially disable fullscreen for + // short pages in which this bug does not occur. It should be removed once + // the page can be reloaded without resizing. + disabling_threshold += safe_area_insets_.top + safe_area_insets_.bottom; } // Don't disable fullscreen if both heights have not been received.
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer.mm index 41a8611..e083962 100644 --- a/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer.mm +++ b/ios/chrome/browser/ui/fullscreen/fullscreen_web_view_resizer.mm
@@ -93,6 +93,7 @@ return; [self updateForInsets:self.model->GetToolbarInsetsAtProgress(progress)]; + self.model->SetWebViewSafeAreaInsets(self.webState->GetView().safeAreaInsets); } // Updates the WebState view, resizing it such as |insets| is the insets between
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 6c607f4..7cfab24 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -386,10 +386,6 @@ const base::Feature kMediaDrmPersistentLicense{ "MediaDrmPersistentLicense", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enables the Android MediaRouter implementation using CAF (Cast v3). -const base::Feature kCafMediaRouterImpl{"CafMediaRouterImpl", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Enables the Android Image Reader path for Video decoding(for AVDA and MCVD) const base::Feature kAImageReaderVideoOutput{"AImageReaderVideoOutput", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 4db370e..bd8b8cd 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -147,7 +147,6 @@ MEDIA_EXPORT extern const base::Feature kVideoFullscreenOrientationLock; MEDIA_EXPORT extern const base::Feature kVideoRotateToFullscreen; MEDIA_EXPORT extern const base::Feature kMediaDrmPersistentLicense; -MEDIA_EXPORT extern const base::Feature kCafMediaRouterImpl; MEDIA_EXPORT extern const base::Feature kAImageReaderVideoOutput; #endif // defined(OS_ANDROID)
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn index fd588806..2ff140b 100644 --- a/media/capture/BUILD.gn +++ b/media/capture/BUILD.gn
@@ -260,6 +260,10 @@ "video/chromeos/display_rotation_observer.h", "video/chromeos/pixel_format_utils.cc", "video/chromeos/pixel_format_utils.h", + "video/chromeos/request_builder.cc", + "video/chromeos/request_builder.h", + "video/chromeos/request_manager.cc", + "video/chromeos/request_manager.h", "video/chromeos/stream_buffer_manager.cc", "video/chromeos/stream_buffer_manager.h", "video/chromeos/video_capture_device_chromeos_halv3.cc", @@ -391,7 +395,7 @@ "video/chromeos/mock_camera_module.h", "video/chromeos/mock_video_capture_client.cc", "video/chromeos/mock_video_capture_client.h", - "video/chromeos/stream_buffer_manager_unittest.cc", + "video/chromeos/request_manager_unittest.cc", ] deps += [ "//build/config/linux/libdrm",
diff --git a/media/capture/video/chromeos/camera_3a_controller.h b/media/capture/video/chromeos/camera_3a_controller.h index 8d3226a..681638aa 100644 --- a/media/capture/video/chromeos/camera_3a_controller.h +++ b/media/capture/video/chromeos/camera_3a_controller.h
@@ -10,7 +10,7 @@ #include "base/cancelable_callback.h" #include "media/base/media_export.h" #include "media/capture/video/chromeos/mojo/camera3.mojom.h" -#include "media/capture/video/chromeos/stream_buffer_manager.h" +#include "media/capture/video/chromeos/request_manager.h" namespace media {
diff --git a/media/capture/video/chromeos/camera_3a_controller_unittest.cc b/media/capture/video/chromeos/camera_3a_controller_unittest.cc index 518ef74..8a39e65 100644 --- a/media/capture/video/chromeos/camera_3a_controller_unittest.cc +++ b/media/capture/video/chromeos/camera_3a_controller_unittest.cc
@@ -8,7 +8,7 @@ #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "media/capture/video/chromeos/camera_metadata_utils.h" -#include "media/capture/video/chromeos/stream_buffer_manager.h" +#include "media/capture/video/chromeos/request_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/capture/video/chromeos/camera_device_context.h b/media/capture/video/chromeos/camera_device_context.h index 83e34da..a4fb232 100644 --- a/media/capture/video/chromeos/camera_device_context.h +++ b/media/capture/video/chromeos/camera_device_context.h
@@ -122,7 +122,7 @@ int GetCameraFrameOrientation(); private: - friend class StreamBufferManagerTest; + friend class RequestManagerTest; SEQUENCE_CHECKER(sequence_checker_);
diff --git a/media/capture/video/chromeos/camera_device_delegate.cc b/media/capture/video/chromeos/camera_device_delegate.cc index 9ef046e..ceac453 100644 --- a/media/capture/video/chromeos/camera_device_delegate.cc +++ b/media/capture/video/chromeos/camera_device_delegate.cc
@@ -22,16 +22,20 @@ #include "media/capture/video/chromeos/camera_device_context.h" #include "media/capture/video/chromeos/camera_hal_delegate.h" #include "media/capture/video/chromeos/camera_metadata_utils.h" -#include "media/capture/video/chromeos/stream_buffer_manager.h" +#include "media/capture/video/chromeos/request_manager.h" namespace media { namespace { -void GetMaxBlobStreamResolution( +// The result of max_width and max_height could be zero if the stream +// is not in the pre-defined configuration. +void GetMaxStreamResolution( const cros::mojom::CameraMetadataPtr& static_metadata, - int32_t* max_blob_width, - int32_t* max_blob_height) { + cros::mojom::Camera3StreamType stream_type, + cros::mojom::HalPixelFormat stream_format, + int32_t* max_width, + int32_t* max_height) { const cros::mojom::CameraMetadataEntryPtr* stream_configurations = GetMetadataEntry(static_metadata, cros::mojom::CameraMetadataTag:: @@ -46,8 +50,8 @@ const size_t kStreamConfigurationSize = 4; int32_t* iter = reinterpret_cast<int32_t*>((*stream_configurations)->data.data()); - *max_blob_width = 0; - *max_blob_height = 0; + *max_width = 0; + *max_height = 0; for (size_t i = 0; i < (*stream_configurations)->count; i += kStreamConfigurationSize) { auto format = @@ -58,17 +62,17 @@ static_cast<cros::mojom::Camera3StreamType>(iter[kStreamTypeOffset]); iter += kStreamConfigurationSize; - if (type != cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT || - format != cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB) { + if (type != stream_type || format != stream_format) { continue; } - if (width > *max_blob_width && height > *max_blob_height) { - *max_blob_width = width; - *max_blob_height = height; + + // TODO(wtlee): Once we have resolution settings mechanism, we could set + // stream resolution based on user's settings. + if (width > *max_width && height > *max_height) { + *max_width = width; + *max_height = height; } } - DCHECK_GT(*max_blob_width, 0); - DCHECK_GT(*max_blob_height, 0); } // VideoCaptureDevice::TakePhotoCallback is given by the application and is used @@ -85,6 +89,17 @@ } // namespace +StreamType StreamIdToStreamType(uint64_t stream_id) { + switch (stream_id) { + case 0: + return StreamType::kPreview; + case 1: + return StreamType::kStillCapture; + default: + return StreamType::kUnknown; + } +} // namespace media + std::string StreamTypeToString(StreamType stream_type) { switch (stream_type) { case StreamType::kPreview: @@ -112,21 +127,6 @@ base::WeakPtr<CameraDeviceDelegate> camera_device_delegate) : camera_device_delegate_(std::move(camera_device_delegate)) {} - void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - std::vector<StreamCaptureInterface::Plane> planes, - base::OnceCallback<void(int32_t)> callback) final { - if (camera_device_delegate_) { - camera_device_delegate_->RegisterBuffer( - buffer_id, type, drm_format, hal_pixel_format, width, height, - std::move(planes), std::move(callback)); - } - } - void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr request, base::OnceCallback<void(int32_t)> callback) final { if (camera_device_delegate_) { @@ -179,10 +179,10 @@ if (!device_context_ || device_context_->GetState() == CameraDeviceContext::State::kStopped || (device_context_->GetState() == CameraDeviceContext::State::kError && - !stream_buffer_manager_)) { + !request_manager_)) { // In case of Mojo connection error the device may be stopped before // StopAndDeAllocate is called; in case of device open failure, the state - // is set to kError and |stream_buffer_manager_| is uninitialized. + // is set to kError and |request_manager_| is uninitialized. std::move(device_close_callback).Run(); return; } @@ -197,7 +197,7 @@ // The device delegate is in the process of opening the camera device. return; } - stream_buffer_manager_->StopPreview(base::NullCallback()); + request_manager_->StopPreview(base::NullCallback()); device_ops_->Close( base::BindOnce(&CameraDeviceDelegate::OnClosed, GetWeakPtr())); } @@ -233,8 +233,10 @@ } int32_t max_blob_width = 0, max_blob_height = 0; - GetMaxBlobStreamResolution(static_metadata_, &max_blob_width, - &max_blob_height); + GetMaxStreamResolution(static_metadata_, + cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT, + cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB, + &max_blob_width, &max_blob_height); photo_state->width->current = max_blob_width; photo_state->width->min = max_blob_width; photo_state->width->max = max_blob_width; @@ -263,8 +265,8 @@ return; } - if (stream_buffer_manager_->GetStreamNumber() < kMaxConfiguredStreams) { - stream_buffer_manager_->StopPreview( + if (request_manager_->GetNumberOfStreams() < kMaxConfiguredStreams) { + request_manager_->StopPreview( base::BindOnce(&CameraDeviceDelegate::OnFlushed, GetWeakPtr())); set_photo_option_callback_ = std::move(callback); } else { @@ -290,7 +292,7 @@ base::BindOnce(&CameraDeviceDelegate::ConstructDefaultRequestSettings, GetWeakPtr(), StreamType::kStillCapture); - if (stream_buffer_manager_->GetStreamNumber() >= kMaxConfiguredStreams) { + if (request_manager_->GetNumberOfStreams() >= kMaxConfiguredStreams) { camera_3a_controller_->Stabilize3AForStillCapture( std::move(construct_request_cb)); return; @@ -317,8 +319,8 @@ OnClosed(0); } else { // The Mojo channel terminated unexpectedly. - if (stream_buffer_manager_) { - stream_buffer_manager_->StopPreview(base::NullCallback()); + if (request_manager_) { + request_manager_->StopPreview(base::NullCallback()); } device_context_->SetState(CameraDeviceContext::State::kStopped); device_context_->SetErrorState( @@ -363,7 +365,7 @@ device_ops_.reset(); camera_3a_controller_.reset(); - stream_buffer_manager_.reset(); + request_manager_.reset(); } void CameraDeviceDelegate::OnGotCameraInfo( @@ -445,13 +447,13 @@ cros::mojom::Camera3CallbackOpsPtr callback_ops_ptr; cros::mojom::Camera3CallbackOpsRequest callback_ops_request = mojo::MakeRequest(&callback_ops_ptr); - stream_buffer_manager_ = std::make_unique<StreamBufferManager>( + request_manager_ = std::make_unique<RequestManager>( std::move(callback_ops_request), std::make_unique<StreamCaptureInterfaceImpl>(GetWeakPtr()), device_context_, std::make_unique<CameraBufferFactory>(), base::BindRepeating(&RotateAndBlobify), ipc_task_runner_); camera_3a_controller_ = std::make_unique<Camera3AController>( - static_metadata_, stream_buffer_manager_.get(), ipc_task_runner_); + static_metadata_, request_manager_.get(), ipc_task_runner_); device_ops_->Initialize( std::move(callback_ops_ptr), base::BindOnce(&CameraDeviceDelegate::OnInitialized, GetWeakPtr())); @@ -509,8 +511,10 @@ // should be configured dynamically per the photo options. if (require_photo) { int32_t max_blob_width = 0, max_blob_height = 0; - GetMaxBlobStreamResolution(static_metadata_, &max_blob_width, - &max_blob_height); + GetMaxStreamResolution( + static_metadata_, cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT, + cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB, &max_blob_width, + &max_blob_height); cros::mojom::Camera3StreamPtr still_capture_stream = cros::mojom::Camera3Stream::New(); @@ -565,7 +569,7 @@ return; } - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( chrome_capture_params_.requested_format, static_metadata_, std::move(updated_config->streams)); @@ -615,7 +619,7 @@ } device_context_->SetState(CameraDeviceContext::State::kCapturing); camera_3a_controller_->SetAutoFocusModeForStillCapture(); - stream_buffer_manager_->StartPreview(std::move(settings)); + request_manager_->StartPreview(std::move(settings)); if (!take_photo_callbacks_.empty()) { TakePhotoImpl(); @@ -631,7 +635,7 @@ DCHECK(ipc_task_runner_->BelongsToCurrentThread()); while (!take_photo_callbacks_.empty()) { - stream_buffer_manager_->TakePhoto( + request_manager_->TakePhoto( settings.Clone(), base::BindOnce( &TakePhotoCallbackBundle, std::move(take_photo_callbacks_.front()), @@ -641,36 +645,6 @@ } } -void CameraDeviceDelegate::RegisterBuffer( - uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - std::vector<StreamCaptureInterface::Plane> planes, - base::OnceCallback<void(int32_t)> callback) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (device_context_->GetState() != CameraDeviceContext::State::kCapturing) { - DCHECK_EQ(device_context_->GetState(), - CameraDeviceContext::State::kStopping); - return; - } - size_t num_planes = planes.size(); - std::vector<mojo::ScopedHandle> fds(num_planes); - std::vector<uint32_t> strides(num_planes); - std::vector<uint32_t> offsets(num_planes); - for (size_t i = 0; i < num_planes; ++i) { - fds[i] = std::move(planes[i].fd); - strides[i] = planes[i].stride; - offsets[i] = planes[i].offset; - } - device_ops_->RegisterBuffer( - buffer_id, type, std::move(fds), drm_format, hal_pixel_format, width, - height, std::move(strides), std::move(offsets), std::move(callback)); -} - void CameraDeviceDelegate::ProcessCaptureRequest( cros::mojom::Camera3CaptureRequestPtr request, base::OnceCallback<void(int32_t)> callback) {
diff --git a/media/capture/video/chromeos/camera_device_delegate.h b/media/capture/video/chromeos/camera_device_delegate.h index 49a0ae0..a94abbc 100644 --- a/media/capture/video/chromeos/camera_device_delegate.h +++ b/media/capture/video/chromeos/camera_device_delegate.h
@@ -20,7 +20,7 @@ class Camera3AController; class CameraDeviceContext; class CameraHalDelegate; -class StreamBufferManager; +class RequestManager; enum class StreamType : uint64_t { kPreview = 0, @@ -28,6 +28,8 @@ kUnknown, }; +StreamType StreamIdToStreamType(uint64_t stream_id); + std::string StreamTypeToString(StreamType stream_type); std::ostream& operator<<(std::ostream& os, StreamType stream_type); @@ -46,16 +48,6 @@ virtual ~StreamCaptureInterface() {} - // Registers a buffer to the camera HAL. - virtual void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - std::vector<Plane> planes, - base::OnceCallback<void(int32_t)> callback) = 0; - // Sends a capture request to the camera HAL. virtual void ProcessCaptureRequest( cros::mojom::Camera3CaptureRequestPtr request, @@ -153,14 +145,6 @@ // StreamCaptureInterface implementations. These methods are called by // |stream_buffer_manager_| on |ipc_task_runner_|. - void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - std::vector<StreamCaptureInterface::Plane> planes, - base::OnceCallback<void(int32_t)> callback); void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr request, base::OnceCallback<void(int32_t)> callback); void Flush(base::OnceCallback<void(int32_t)> callback); @@ -180,7 +164,7 @@ std::queue<VideoCaptureDevice::TakePhotoCallback> take_photo_callbacks_; - std::unique_ptr<StreamBufferManager> stream_buffer_manager_; + std::unique_ptr<RequestManager> request_manager_; std::unique_ptr<Camera3AController> camera_3a_controller_;
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc index 2d565c2..41e6c60 100644 --- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc +++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -88,21 +88,7 @@ uint32_t height, const std::vector<uint32_t>& strides, const std::vector<uint32_t>& offsets, - RegisterBufferCallback callback) override { - DoRegisterBuffer(buffer_id, type, fds, drm_format, hal_pixel_format, width, - height, strides, offsets, callback); - } - MOCK_METHOD10(DoRegisterBuffer, - void(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - std::vector<mojo::ScopedHandle>& fds, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - const std::vector<uint32_t>& strides, - const std::vector<uint32_t>& offsets, - RegisterBufferCallback& callback)); + RegisterBufferCallback callback) override {} void Close(CloseCallback callback) override { DoClose(callback); } MOCK_METHOD1(DoClose, void(CloseCallback& callback)); @@ -265,19 +251,6 @@ std::move(callback).Run(std::move(fake_settings)); } - void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - std::vector<mojo::ScopedHandle>& fds, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - const std::vector<uint32_t>& strides, - const std::vector<uint32_t>& offsets, - base::OnceCallback<void(int32_t)>& callback) { - std::move(callback).Run(0); - } - void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { std::move(callback).Run(0); @@ -378,12 +351,6 @@ } void SetUpExpectationForCaptureLoop() { - EXPECT_CALL(mock_camera_device_, - DoRegisterBuffer(_, _, _, _, _, _, _, _, _, _)) - .Times(AtLeast(1)) - .WillOnce(Invoke(this, &CameraDeviceDelegateTest::RegisterBuffer)) - .WillRepeatedly( - Invoke(this, &CameraDeviceDelegateTest::RegisterBuffer)); EXPECT_CALL(mock_camera_device_, DoProcessCaptureRequest(_, _)) .Times(AtLeast(1)) .WillOnce(
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc index 151d58ee..c85897d 100644 --- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc +++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -21,6 +21,7 @@ #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/trace_event/trace_event.h" +#include "media/capture/video/chromeos/mojo/camera_common.mojom.h" #include "mojo/public/cpp/platform/named_platform_channel.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/platform/socket_utils_posix.h"
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc index e8eb1a5f..f6feeec8 100644 --- a/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc +++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl_unittest.cc
@@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/test/scoped_task_environment.h" +#include "media/capture/video/chromeos/mojo/camera_common.mojom.h" #include "media/capture/video/chromeos/mojo/cros_camera_service.mojom.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/media/capture/video/chromeos/mojo/camera3.mojom b/media/capture/video/chromeos/mojo/camera3.mojom index 2cba342..1ce2e11 100644 --- a/media/capture/video/chromeos/mojo/camera3.mojom +++ b/media/capture/video/chromeos/mojo/camera3.mojom
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Next min version: 2 +// Next min version: 3 module cros.mojom; @@ -73,12 +73,25 @@ CAMERA3_BUFFER_STATUS_ERROR = 1, }; +// Structure that contains needed information about a camera buffer. +struct CameraBufferHandle { + uint64 buffer_id; + array<handle> fds; + uint32 drm_format; + HalPixelFormat hal_pixel_format; + uint32 width; + uint32 height; + array<uint32> strides; + array<uint32> offsets; +}; + struct Camera3StreamBuffer { uint64 stream_id; uint64 buffer_id; Camera3BufferStatus status; handle? acquire_fence; handle? release_fence; + [MinVersion=2] CameraBufferHandle? buffer_handle; }; enum Camera3MsgType { @@ -158,9 +171,8 @@ }; // Camera3DeviceOps is mostly a translation of the camera3_device_ops_t API from -// Android camera HAL v3, with the additional RegisterBuffer() function to pass -// buffer handles across processes. This is the interface to interact with a -// camera device in the camera HAL. +// Android camera HAL v3. This is the interface to interact with a camera +// device in the camera HAL. // // The work flow of the Camera3DeviceOps is: // @@ -178,21 +190,15 @@ // 4. Start the capture loop. The capture loop is composed of a series of // capture requests and results. // -// For each capture request: -// a. Call RegisterBuffer() for each buffer associated with the request -// to register the buffers which will later be filled by the camera -// HAL with capture result. For example, the client may register one -// small buffer for the low-resolution preview stream and one large -// buffer for the high-resolution still capture stream. -// b. Call ProcessCaptureRequest() to request capturing one frame. A -// request may contain multiple streams and the camera HAL would fill -// the buffers of each streams per requirements specified in -// ConfigureStreams() and RegisterBuffer(). For example, the camera -// HAL may fill a frame to a still capture buffer with the native -// capture resolution, and down-scale the same frame to a lower -// resolution for the preview buffer. -// The client may continue with RegisterBuffer() -> ProcessCaptureRequest() -// up to the pipe-line depth configured in ConfigureStreams(). +// The capture loop shall call ProcessCaptureRequest() to request capturing +// each frame. A request may contain multiple streams and the camera HAL +// would fill the buffers of each streams per requirements specified in +// ConfigureStreams(). For example, the camera HAL may fill a frame to a +// still capture buffer with the native capture resolution, and down-scale +// the same frame to a lower resolution for the preview buffer. +// +// The client may continue calling ProcessCaptureRequest() up to the +// pipe-line depth configured in ConfigureStreams(). // // When the camera HAL is done with a capture request, the capture result // is sent back to the client through the callbacks in Camera3CallbackOps. @@ -247,6 +253,8 @@ // camera HAL to idle state. Flush@5() => (int32 result); + // [Deprecated in version 2] + // // The type of buffers the CrOS camera service currently supports. // GRALLOC is for the platform-specific gralloc buffer allocated by Android. // SHM is for the shared memory buffer allocated by Chrome. @@ -256,6 +264,8 @@ // Add DMABUF when needed. }; + // [Deprecated in version 2] + // // RegisterBuffer() is called to register a buffer with the camera HAL. The // registered buffer can then be specified in ProcessCaptureRequest() for the // camera HAL to fill captured frame. RegisterBuffer() is not part of the
diff --git a/media/capture/video/chromeos/request_builder.cc b/media/capture/video/chromeos/request_builder.cc new file mode 100644 index 0000000..b55a93a --- /dev/null +++ b/media/capture/video/chromeos/request_builder.cc
@@ -0,0 +1,96 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/capture/video/chromeos/request_builder.h" + +#include <utility> + +#include "media/capture/video/chromeos/camera_device_context.h" +#include "mojo/public/cpp/platform/platform_handle.h" +#include "mojo/public/cpp/system/platform_handle.h" + +namespace media { + +RequestBuilder::RequestBuilder(CameraDeviceContext* device_context, + RequestBufferCallback request_buffer_callback) + : device_context_(device_context), + request_buffer_callback_(std::move(request_buffer_callback)) {} + +RequestBuilder::~RequestBuilder() = default; + +cros::mojom::Camera3CaptureRequestPtr RequestBuilder::BuildRequest( + std::set<StreamType> stream_types, + cros::mojom::CameraMetadataPtr settings) { + auto capture_request = cros::mojom::Camera3CaptureRequest::New(); + for (StreamType stream_type : stream_types) { + base::Optional<BufferInfo> buffer_info = + request_buffer_callback_.Run(stream_type); + if (!buffer_info) { + return capture_request; + } + const uint64_t buffer_id = buffer_info->id; + auto buffer_handle = CreateCameraBufferHandle(stream_type, *buffer_info); + auto stream_buffer = + CreateStreamBuffer(stream_type, buffer_id, std::move(buffer_handle)); + capture_request->output_buffers.push_back(std::move(stream_buffer)); + } + + capture_request->settings = std::move(settings); + return capture_request; +} + +cros::mojom::CameraBufferHandlePtr RequestBuilder::CreateCameraBufferHandle( + StreamType stream_type, + BufferInfo buffer_info) { + auto buffer_handle = cros::mojom::CameraBufferHandle::New(); + + buffer_handle->buffer_id = buffer_info.id; + buffer_handle->drm_format = buffer_info.drm_format; + buffer_handle->hal_pixel_format = buffer_info.hal_pixel_format; + buffer_handle->width = buffer_info.gpu_memory_buffer->GetSize().width(); + buffer_handle->height = buffer_info.gpu_memory_buffer->GetSize().height(); + + gfx::NativePixmapHandle native_pixmap_handle = + buffer_info.gpu_memory_buffer->CloneHandle().native_pixmap_handle; + + size_t num_planes = native_pixmap_handle.planes.size(); + DCHECK_EQ(num_planes, native_pixmap_handle.fds.size()); + // Take ownership of fds. + std::vector<base::ScopedFD> fds(num_planes); + for (size_t i = 0; i < num_planes; ++i) + fds[i] = base::ScopedFD(native_pixmap_handle.fds[i].fd); + + std::vector<StreamCaptureInterface::Plane> planes(num_planes); + for (size_t i = 0; i < num_planes; ++i) { + mojo::ScopedHandle mojo_fd = + mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fds[i]))); + if (!mojo_fd.is_valid()) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerFailedToWrapGpuMemoryHandle, + FROM_HERE, "Failed to wrap gpu memory handle"); + return nullptr; + } + buffer_handle->fds.push_back(std::move(mojo_fd)); + buffer_handle->strides.push_back(native_pixmap_handle.planes[i].stride); + buffer_handle->offsets.push_back(native_pixmap_handle.planes[i].offset); + } + + return buffer_handle; +} + +cros::mojom::Camera3StreamBufferPtr RequestBuilder::CreateStreamBuffer( + StreamType stream_type, + uint64_t buffer_id, + cros::mojom::CameraBufferHandlePtr buffer_handle) { + cros::mojom::Camera3StreamBufferPtr buffer = + cros::mojom::Camera3StreamBuffer::New(); + buffer->stream_id = static_cast<uint64_t>(stream_type); + buffer->buffer_id = buffer_id; + buffer->status = cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK; + buffer->buffer_handle = std::move(buffer_handle); + return buffer; +} + +} // namespace media
diff --git a/media/capture/video/chromeos/request_builder.h b/media/capture/video/chromeos/request_builder.h new file mode 100644 index 0000000..6dbf901 --- /dev/null +++ b/media/capture/video/chromeos/request_builder.h
@@ -0,0 +1,66 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_BUILDER_H_ +#define MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_BUILDER_H_ + +#include <memory> +#include <set> +#include <vector> + +#include "base/optional.h" +#include "media/capture/video/chromeos/camera_device_delegate.h" +#include "media/capture/video/chromeos/mojo/camera3.mojom.h" +#include "media/capture/video_capture_types.h" +#include "mojo/public/cpp/bindings/binding.h" + +namespace media { + +class CameraDeviceContext; + +// BufferInfo is used to store information about the buffer that is needed when +// building buffers. +struct BufferInfo { + uint64_t id; + const gfx::GpuMemoryBuffer* gpu_memory_buffer; + uint32_t drm_format; + cros::mojom::HalPixelFormat hal_pixel_format; +}; + +// RequestBuilder is used to build capture request that will be sent to camera +// HAL process. +class CAPTURE_EXPORT RequestBuilder { + public: + using RequestBufferCallback = + base::RepeatingCallback<base::Optional<BufferInfo>(StreamType)>; + + RequestBuilder(CameraDeviceContext* device_context, + // Callback to request buffer from StreamBufferManager. Having + // this callback, we do not need to include StreamBufferManager + // when requesting buffer. + RequestBufferCallback request_buffer_callback); + ~RequestBuilder(); + + // Builds a capture request by given streams and settings. + cros::mojom::Camera3CaptureRequestPtr BuildRequest( + std::set<StreamType> stream_types, + cros::mojom::CameraMetadataPtr settings); + + private: + cros::mojom::CameraBufferHandlePtr CreateCameraBufferHandle( + StreamType stream_type, + BufferInfo buffer_info); + + cros::mojom::Camera3StreamBufferPtr CreateStreamBuffer( + StreamType stream_type, + uint64_t buffer_id, + cros::mojom::CameraBufferHandlePtr buffer_handle); + + CameraDeviceContext* device_context_; + + RequestBufferCallback request_buffer_callback_; +}; +} // namespace media + +#endif // MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_BUILDER_H_
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc new file mode 100644 index 0000000..3a165f7 --- /dev/null +++ b/media/capture/video/chromeos/request_manager.cc
@@ -0,0 +1,655 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/capture/video/chromeos/request_manager.h" + +#include <sync/sync.h> +#include <map> +#include <set> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/posix/safe_strerror.h" +#include "base/trace_event/trace_event.h" +#include "media/capture/video/chromeos/camera_buffer_factory.h" +#include "media/capture/video/chromeos/camera_device_context.h" +#include "media/capture/video/chromeos/camera_metadata_utils.h" +#include "mojo/public/cpp/platform/platform_handle.h" +#include "mojo/public/cpp/system/platform_handle.h" + +namespace media { + +namespace { + +constexpr uint32_t kUndefinedFrameNumber = 0xFFFFFFFF; +} // namespace + +RequestManager::RequestManager( + cros::mojom::Camera3CallbackOpsRequest callback_ops_request, + std::unique_ptr<StreamCaptureInterface> capture_interface, + CameraDeviceContext* device_context, + std::unique_ptr<CameraBufferFactory> camera_buffer_factory, + BlobifyCallback blobify_callback, + scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) + : callback_ops_(this, std::move(callback_ops_request)), + capture_interface_(std::move(capture_interface)), + device_context_(device_context), + stream_buffer_manager_( + new StreamBufferManager(device_context_, + std::move(camera_buffer_factory))), + blobify_callback_(std::move(blobify_callback)), + ipc_task_runner_(std::move(ipc_task_runner)), + capturing_(false), + frame_number_(0), + partial_result_count_(1), + first_frame_shutter_time_(base::TimeTicks()), + weak_ptr_factory_(this) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + DCHECK(callback_ops_.is_bound()); + DCHECK(device_context_); + // We use base::Unretained() for the StreamBufferManager here since we + // guarantee |request_buffer_callback| is only used by RequestBuilder. In + // addition, since C++ destroys member variables in reverse order of + // construction, we can ensure that RequestBuilder will be destroyed prior + // to StreamBufferManager since RequestBuilder constructs after + // StreamBufferManager. + auto request_buffer_callback = + base::BindRepeating(&StreamBufferManager::RequestBuffer, + base::Unretained(stream_buffer_manager_.get())); + request_builder_ = std::make_unique<RequestBuilder>( + device_context_, std::move(request_buffer_callback)); +} + +RequestManager::~RequestManager() = default; + +void RequestManager::SetUpStreamsAndBuffers( + VideoCaptureFormat capture_format, + const cros::mojom::CameraMetadataPtr& static_metadata, + std::vector<cros::mojom::Camera3StreamPtr> streams) { + // The partial result count metadata is optional; defaults to 1 in case it + // is not set in the static metadata. + const cros::mojom::CameraMetadataEntryPtr* partial_count = GetMetadataEntry( + static_metadata, + cros::mojom::CameraMetadataTag::ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (partial_count) { + partial_result_count_ = + *reinterpret_cast<int32_t*>((*partial_count)->data.data()); + } + + // Set the last received frame number for each stream types to be undefined. + for (const auto& stream : streams) { + StreamType stream_type = StreamIdToStreamType(stream->id); + last_received_frame_number_map_[stream_type] = kUndefinedFrameNumber; + } + + stream_buffer_manager_->SetUpStreamsAndBuffers( + capture_format, std::move(static_metadata), std::move(streams)); +} + +cros::mojom::Camera3StreamPtr RequestManager::GetStreamConfiguration( + StreamType stream_type) { + return stream_buffer_manager_->GetStreamConfiguration(stream_type); +} + +void RequestManager::StartPreview( + cros::mojom::CameraMetadataPtr preview_settings) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + DCHECK(repeating_request_settings_.is_null()); + + capturing_ = true; + repeating_request_settings_ = std::move(preview_settings); + + PrepareCaptureRequest(); +} + +void RequestManager::StopPreview(base::OnceCallback<void(int32_t)> callback) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + capturing_ = false; + repeating_request_settings_ = nullptr; + if (callback) { + capture_interface_->Flush(std::move(callback)); + } +} + +void RequestManager::TakePhoto(cros::mojom::CameraMetadataPtr settings, + VideoCaptureDevice::TakePhotoCallback callback) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + std::vector<uint8_t> frame_orientation(sizeof(int32_t)); + *reinterpret_cast<int32_t*>(frame_orientation.data()) = + base::checked_cast<int32_t>(device_context_->GetCameraFrameOrientation()); + cros::mojom::CameraMetadataEntryPtr e = + cros::mojom::CameraMetadataEntry::New(); + e->tag = cros::mojom::CameraMetadataTag::ANDROID_JPEG_ORIENTATION; + e->type = cros::mojom::EntryType::TYPE_INT32; + e->count = 1; + e->data = std::move(frame_orientation); + AddOrUpdateMetadataEntry(&settings, std::move(e)); + + oneshot_request_settings_.push(std::move(settings)); + take_photo_callback_queue_.push(std::move(callback)); +} + +void RequestManager::PrepareCaptureRequest() { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + if (!capturing_) { + return; + } + std::set<StreamType> stream_types; + cros::mojom::CameraMetadataPtr settings; + + // Reqular request should always have repeating request if the preview is + // on. + stream_types.insert(StreamType::kPreview); + if (!stream_buffer_manager_->HasFreeBuffers(stream_types)) { + return; + } + bool has_still_capture_streams = false; + if (!oneshot_request_settings_.empty() && + stream_buffer_manager_->HasFreeBuffers({StreamType::kStillCapture})) { + stream_types.insert(StreamType::kStillCapture); + settings = std::move(oneshot_request_settings_.front()); + oneshot_request_settings_.pop(); + has_still_capture_streams = true; + } else { + settings = repeating_request_settings_.Clone(); + } + + auto capture_request = request_builder_->BuildRequest(std::move(stream_types), + std::move(settings)); + if (has_still_capture_streams) { + SendCaptureRequest(std::move(capture_request), + std::move(take_photo_callback_queue_.front())); + take_photo_callback_queue_.pop(); + } else { + SendCaptureRequest(std::move(capture_request), base::DoNothing()); + } +} + +void RequestManager::SendCaptureRequest( + cros::mojom::Camera3CaptureRequestPtr capture_request, + VideoCaptureDevice::TakePhotoCallback take_photo_callback) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + if (!capturing_) { + return; + } + + CaptureResult& pending_result = pending_results_[frame_number_]; + pending_result.still_capture_callback = std::move(take_photo_callback); + pending_result.unsubmitted_buffer_count = + capture_request->output_buffers.size(); + + UpdateCaptureSettings(&capture_request->settings); + capture_request->frame_number = frame_number_; + capture_interface_->ProcessCaptureRequest( + std::move(capture_request), + base::BindOnce(&RequestManager::OnProcessedCaptureRequest, + weak_ptr_factory_.GetWeakPtr())); + frame_number_++; +} + +void RequestManager::OnProcessedCaptureRequest(int32_t result) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + if (!capturing_) { + return; + } + if (result != 0) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerProcessCaptureRequestFailed, + FROM_HERE, + std::string("Process capture request failed: ") + + base::safe_strerror(-result)); + return; + } + + PrepareCaptureRequest(); +} + +void RequestManager::ProcessCaptureResult( + cros::mojom::Camera3CaptureResultPtr result) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + if (!capturing_) { + return; + } + uint32_t frame_number = result->frame_number; + // A new partial result may be created in either ProcessCaptureResult or + // Notify. + CaptureResult& pending_result = pending_results_[frame_number]; + + // |result->partial_result| is set to 0 if the capture result contains only + // the result buffer handles and no result metadata. + if (result->partial_result != 0) { + uint32_t result_id = result->partial_result; + if (result_id > partial_result_count_) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerInvalidPendingResultId, + FROM_HERE, + std::string("Invalid pending_result id: ") + + std::to_string(result_id)); + return; + } + if (pending_result.partial_metadata_received.count(result_id)) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerReceivedDuplicatedPartialMetadata, + FROM_HERE, + std::string("Received duplicated partial metadata: ") + + std::to_string(result_id)); + return; + } + DVLOG(2) << "Received partial result " << result_id << " for frame " + << frame_number; + pending_result.partial_metadata_received.insert(result_id); + MergeMetadata(&pending_result.metadata, result->result); + } + + if (result->output_buffers) { + if (result->output_buffers->size() > kMaxConfiguredStreams) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerIncorrectNumberOfOutputBuffersReceived, + FROM_HERE, + std::string("Incorrect number of output buffers received: ") + + std::to_string(result->output_buffers->size())); + return; + } + + for (auto& stream_buffer : result->output_buffers.value()) { + DVLOG(2) << "Received capture result for frame " << frame_number + << " stream_id: " << stream_buffer->stream_id; + StreamType stream_type = StreamIdToStreamType(stream_buffer->stream_id); + if (stream_type == StreamType::kUnknown) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerInvalidTypeOfOutputBuffersReceived, + FROM_HERE, + std::string("Invalid type of output buffers received: ") + + std::to_string(stream_buffer->stream_id)); + return; + } + + // The camera HAL v3 API specifies that only one capture result can carry + // the result buffer for any given frame number. + if (last_received_frame_number_map_[stream_type] == + kUndefinedFrameNumber) { + last_received_frame_number_map_[stream_type] = frame_number; + } else { + if (last_received_frame_number_map_[stream_type] == frame_number) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerReceivedMultipleResultBuffersForFrame, + FROM_HERE, + std::string("Received multiple result buffers for frame ") + + std::to_string(frame_number) + std::string(" for stream ") + + std::to_string(stream_buffer->stream_id)); + return; + } else if (last_received_frame_number_map_[stream_type] > + frame_number) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerReceivedFrameIsOutOfOrder, + FROM_HERE, + std::string("Received frame is out-of-order; expect frame number " + "greater than ") + + std::to_string(last_received_frame_number_map_[stream_type]) + + std::string(" but got ") + std::to_string(frame_number)); + } else { + last_received_frame_number_map_[stream_type] = frame_number; + } + } + + if (stream_buffer->status == + cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) { + // If the buffer is marked as error, its content is discarded for this + // frame. Send the buffer to the free list directly through + // SubmitCaptureResult. + SubmitCaptureResult(frame_number, stream_type, + std::move(stream_buffer)); + } else { + pending_result.buffers[stream_type] = std::move(stream_buffer); + } + } + } + + TRACE_EVENT1("camera", "Capture Result", "frame_number", frame_number); + TrySubmitPendingBuffers(frame_number); +} + +void RequestManager::TrySubmitPendingBuffers(uint32_t frame_number) { + if (!pending_results_.count(frame_number)) { + return; + } + + CaptureResult& pending_result = pending_results_[frame_number]; + + // If the metadata is not ready, or the shutter time is not set, just + // returned. + bool is_ready_to_submit = + pending_result.partial_metadata_received.size() > 0 && + *pending_result.partial_metadata_received.rbegin() == + partial_result_count_ && + !pending_result.reference_time.is_null(); + if (!is_ready_to_submit) { + return; + } + + if (!pending_result.buffers.empty()) { + // Put pending buffers into local map since |pending_result| might be + // deleted in SubmitCaptureResult(). We should not reference pending_result + // after SubmitCaptureResult() is triggered. + std::map<StreamType, cros::mojom::Camera3StreamBufferPtr> buffers = + std::move(pending_result.buffers); + for (auto& it : buffers) { + SubmitCaptureResult(frame_number, it.first, std::move(it.second)); + } + } +} + +void RequestManager::Notify(cros::mojom::Camera3NotifyMsgPtr message) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + if (!capturing_) { + return; + } + if (message->type == cros::mojom::Camera3MsgType::CAMERA3_MSG_ERROR) { + auto error = std::move(message->message->get_error()); + uint32_t frame_number = error->frame_number; + uint64_t error_stream_id = error->error_stream_id; + StreamType stream_type = StreamIdToStreamType(error_stream_id); + if (stream_type == StreamType::kUnknown) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerUnknownStreamInCamera3NotifyMsg, + FROM_HERE, + std::string("Unknown stream in Camera3NotifyMsg: ") + + std::to_string(error_stream_id)); + return; + } + cros::mojom::Camera3ErrorMsgCode error_code = error->error_code; + HandleNotifyError(frame_number, stream_type, error_code); + } else if (message->type == + cros::mojom::Camera3MsgType::CAMERA3_MSG_SHUTTER) { + auto shutter = std::move(message->message->get_shutter()); + uint32_t frame_number = shutter->frame_number; + uint64_t shutter_time = shutter->timestamp; + DVLOG(2) << "Received shutter time for frame " << frame_number; + if (!shutter_time) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerReceivedInvalidShutterTime, + FROM_HERE, + std::string("Received invalid shutter time: ") + + std::to_string(shutter_time)); + return; + } + CaptureResult& pending_result = pending_results_[frame_number]; + // Shutter timestamp is in ns. + base::TimeTicks reference_time = + base::TimeTicks() + + base::TimeDelta::FromMicroseconds(shutter_time / 1000); + pending_result.reference_time = reference_time; + if (first_frame_shutter_time_.is_null()) { + // Record the shutter time of the first frame for calculating the + // timestamp. + first_frame_shutter_time_ = reference_time; + } + pending_result.timestamp = reference_time - first_frame_shutter_time_; + TrySubmitPendingBuffers(frame_number); + } +} + +void RequestManager::HandleNotifyError( + uint32_t frame_number, + StreamType stream_type, + cros::mojom::Camera3ErrorMsgCode error_code) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + std::string warning_msg; + + switch (error_code) { + case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_DEVICE: + // Fatal error and no more frames will be produced by the device. + device_context_->SetErrorState( + media::VideoCaptureError::kCrosHalV3BufferManagerFatalDeviceError, + FROM_HERE, "Fatal device error"); + return; + + case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_REQUEST: + // An error has occurred in processing the request; the request + // specified by |frame_number| has been dropped by the camera device. + // Subsequent requests are unaffected. + // + // The HAL will call ProcessCaptureResult with the buffers' state set to + // STATUS_ERROR. The content of the buffers will be dropped and the + // buffers will be reused in SubmitCaptureResult. + warning_msg = + std::string("An error occurred while processing request for frame ") + + std::to_string(frame_number); + break; + + case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_RESULT: + // An error has occurred in producing the output metadata buffer for a + // result; the output metadata will not be available for the frame + // specified by |frame_number|. Subsequent requests are unaffected. + warning_msg = std::string( + "An error occurred while producing result " + "metadata for frame ") + + std::to_string(frame_number); + break; + + case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_BUFFER: + // An error has occurred in placing the output buffer into a stream for + // a request. |frame_number| specifies the request for which the buffer + // was dropped, and |stream_type| specifies the stream that dropped + // the buffer. + // + // The HAL will call ProcessCaptureResult with the buffer's state set to + // STATUS_ERROR. The content of the buffer will be dropped and the + // buffer will be reused in SubmitCaptureResult. + warning_msg = + std::string( + "An error occurred while filling output buffer of stream ") + + StreamTypeToString(stream_type) + std::string(" in frame ") + + std::to_string(frame_number); + break; + + default: + // To eliminate the warning for not handling CAMERA3_MSG_NUM_ERRORS + break; + } + + LOG(WARNING) << warning_msg << stream_type; + device_context_->LogToClient(warning_msg); + + // If the buffer is already returned by the HAL, submit it and we're done. + if (pending_results_.count(frame_number)) { + auto it = pending_results_[frame_number].buffers.find(stream_type); + if (it != pending_results_[frame_number].buffers.end()) { + auto stream_buffer = std::move(it->second); + pending_results_[frame_number].buffers.erase(stream_type); + SubmitCaptureResult(frame_number, stream_type, std::move(stream_buffer)); + } + } +} + +void RequestManager::SubmitCaptureResult( + uint32_t frame_number, + StreamType stream_type, + cros::mojom::Camera3StreamBufferPtr stream_buffer) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + DCHECK(pending_results_.count(frame_number)); + + CaptureResult& pending_result = pending_results_[frame_number]; + DVLOG(2) << "Submit capture result of frame " << frame_number + << " for stream " << static_cast<int>(stream_type); + for (auto* observer : result_metadata_observers_) { + observer->OnResultMetadataAvailable(pending_result.metadata); + } + uint64_t buffer_id = stream_buffer->buffer_id; + + // Wait on release fence before delivering the result buffer to client. + if (stream_buffer->release_fence.is_valid()) { + const int kSyncWaitTimeoutMs = 1000; + mojo::PlatformHandle fence = + mojo::UnwrapPlatformHandle(std::move(stream_buffer->release_fence)); + if (!fence.is_valid()) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerFailedToUnwrapReleaseFenceFd, + FROM_HERE, "Failed to unwrap release fence fd"); + return; + } + if (!sync_wait(fence.GetFD().get(), kSyncWaitTimeoutMs)) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerSyncWaitOnReleaseFenceTimedOut, + FROM_HERE, "Sync wait on release fence timed out"); + return; + } + } + + // Deliver the captured data to client. + if (stream_buffer->status == + cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK) { + gfx::GpuMemoryBuffer* buffer = + stream_buffer_manager_->GetBufferById(stream_type, buffer_id); + if (stream_type == StreamType::kPreview) { + device_context_->SubmitCapturedData( + buffer, stream_buffer_manager_->GetStreamCaptureFormat(stream_type), + pending_result.reference_time, pending_result.timestamp); + } else if (stream_type == StreamType::kStillCapture) { + DCHECK(pending_result.still_capture_callback); + const Camera3JpegBlob* header = reinterpret_cast<Camera3JpegBlob*>( + reinterpret_cast<uintptr_t>(buffer->memory(0)) + + buffer->GetSize().width() - sizeof(Camera3JpegBlob)); + if (header->jpeg_blob_id != kCamera3JpegBlobId) { + device_context_->SetErrorState( + media::VideoCaptureError::kCrosHalV3BufferManagerInvalidJpegBlob, + FROM_HERE, "Invalid JPEG blob"); + return; + } + // Still capture result from HALv3 already has orientation info in EXIF, + // so just provide 0 as screen rotation in |blobify_callback_| parameters. + mojom::BlobPtr blob = blobify_callback_.Run( + reinterpret_cast<uint8_t*>(buffer->memory(0)), header->jpeg_size, + stream_buffer_manager_->GetStreamCaptureFormat(stream_type), 0); + if (blob) { + std::move(pending_result.still_capture_callback).Run(std::move(blob)); + } else { + // TODO(wtlee): If it is fatal, we should set error state here. + LOG(ERROR) << "Failed to blobify the captured JPEG image"; + } + } + } + stream_buffer_manager_->ReleaseBuffer(stream_type, buffer_id); + pending_result.unsubmitted_buffer_count--; + + if (pending_result.unsubmitted_buffer_count == 0) { + pending_results_.erase(frame_number); + } + // Every time a buffer is released, try to prepare another capture request + // again. + PrepareCaptureRequest(); +} + +size_t RequestManager::GetNumberOfStreams() { + return stream_buffer_manager_->GetNumberOfStreams(); +} + +void RequestManager::AddResultMetadataObserver( + ResultMetadataObserver* observer) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + DCHECK(!result_metadata_observers_.count(observer)); + + result_metadata_observers_.insert(observer); +} + +void RequestManager::RemoveResultMetadataObserver( + ResultMetadataObserver* observer) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + DCHECK(result_metadata_observers_.count(observer)); + + result_metadata_observers_.erase(observer); +} + +void RequestManager::SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + cros::mojom::CameraMetadataEntryPtr setting = + cros::mojom::CameraMetadataEntry::New(); + + setting->tag = tag; + setting->type = type; + setting->count = count; + setting->data = std::move(value); + + capture_settings_override_.push_back(std::move(setting)); +} + +void RequestManager::SetRepeatingCaptureMetadata( + cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + cros::mojom::CameraMetadataEntryPtr setting = + cros::mojom::CameraMetadataEntry::New(); + + setting->tag = tag; + setting->type = type; + setting->count = count; + setting->data = std::move(value); + + capture_settings_repeating_override_[tag] = std::move(setting); +} + +void RequestManager::UnsetRepeatingCaptureMetadata( + cros::mojom::CameraMetadataTag tag) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + auto it = capture_settings_repeating_override_.find(tag); + if (it == capture_settings_repeating_override_.end()) { + LOG(ERROR) << "Unset a non-existent metadata: " << tag; + return; + } + capture_settings_repeating_override_.erase(it); +} + +void RequestManager::UpdateCaptureSettings( + cros::mojom::CameraMetadataPtr* capture_settings) { + DCHECK(ipc_task_runner_->BelongsToCurrentThread()); + + if (capture_settings_override_.empty() && + capture_settings_repeating_override_.empty()) { + return; + } + + for (const auto& setting : capture_settings_repeating_override_) { + AddOrUpdateMetadataEntry(capture_settings, setting.second.Clone()); + } + + for (auto& s : capture_settings_override_) { + AddOrUpdateMetadataEntry(capture_settings, std::move(s)); + } + capture_settings_override_.clear(); + SortCameraMetadata(capture_settings); +} + +RequestManager::CaptureResult::CaptureResult() + : metadata(cros::mojom::CameraMetadata::New()), + unsubmitted_buffer_count(0) {} + +RequestManager::CaptureResult::~CaptureResult() = default; + +} // namespace media
diff --git a/media/capture/video/chromeos/request_manager.h b/media/capture/video/chromeos/request_manager.h new file mode 100644 index 0000000..fb9110b4 --- /dev/null +++ b/media/capture/video/chromeos/request_manager.h
@@ -0,0 +1,302 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_ +#define MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_ + +#include <cstring> +#include <map> +#include <memory> +#include <queue> +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "base/memory/weak_ptr.h" +#include "media/capture/mojom/image_capture.mojom.h" +#include "media/capture/video/chromeos/camera_device_delegate.h" +#include "media/capture/video/chromeos/mojo/camera3.mojom.h" +#include "media/capture/video/chromeos/request_builder.h" +#include "media/capture/video/chromeos/stream_buffer_manager.h" +#include "media/capture/video_capture_types.h" +#include "mojo/public/cpp/bindings/binding.h" + +namespace media { + +class CameraBufferFactory; +class CameraDeviceContext; + +// The JPEG transport header as defined by Android camera HAL v3 API. The JPEG +// transport header is at the end of the blob buffer filled by the HAL. +constexpr uint16_t kCamera3JpegBlobId = 0x00FF; +struct Camera3JpegBlob { + uint16_t jpeg_blob_id; + uint32_t jpeg_size; +}; + +static const int kMaxConfiguredStreams = 2; + +// Interface that provides API to let Camera3AController to update the metadata +// that will be sent with capture request. +class CAPTURE_EXPORT CaptureMetadataDispatcher { + public: + class ResultMetadataObserver { + public: + virtual ~ResultMetadataObserver() {} + virtual void OnResultMetadataAvailable( + const cros::mojom::CameraMetadataPtr&) = 0; + }; + + virtual ~CaptureMetadataDispatcher() {} + virtual void AddResultMetadataObserver(ResultMetadataObserver* observer) = 0; + virtual void RemoveResultMetadataObserver( + ResultMetadataObserver* observer) = 0; + virtual void SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) = 0; + virtual void SetRepeatingCaptureMetadata(cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) = 0; + virtual void UnsetRepeatingCaptureMetadata( + cros::mojom::CameraMetadataTag tag) = 0; +}; + +// RequestManager is responsible for managing the flow for sending capture +// requests and receiving capture results. Having RequestBuilder to build +// request and StreamBufferManager to handles stream buffers, it focuses on +// controlling the capture flow between Chrome and camera HAL process. +class CAPTURE_EXPORT RequestManager final + : public cros::mojom::Camera3CallbackOps, + public CaptureMetadataDispatcher { + public: + using BlobifyCallback = base::RepeatingCallback<mojom::BlobPtr( + const uint8_t* buffer, + const uint32_t bytesused, + const VideoCaptureFormat& capture_format, + int screen_rotation)>; + + // CaptureResult is used to hold the pending capture results for each frame. + struct CaptureResult { + CaptureResult(); + ~CaptureResult(); + // |reference_time| and |timestamp| are derived from the shutter time of + // this frame. They are be passed to |client_->OnIncomingCapturedData| + // along with the |buffers| when the captured frame is submitted. + base::TimeTicks reference_time; + base::TimeDelta timestamp; + // The result metadata. Contains various information about the captured + // frame. + cros::mojom::CameraMetadataPtr metadata; + // The buffer handles that hold the captured data of this frame. + std::map<StreamType, cros::mojom::Camera3StreamBufferPtr> buffers; + // The set of the partial metadata received. For each capture result, the + // total number of partial metadata should equal to + // |partial_result_count_|. + std::set<uint32_t> partial_metadata_received; + // Incremented for every stream buffer requested for the given frame. + // StreamBufferManager destructs the CaptureResult when + // |unsubmitted_buffer_count| drops to zero. + size_t unsubmitted_buffer_count; + // The callback used to return the captured still capture JPEG buffer. Set + // if and only if the capture request was sent with a still capture buffer. + VideoCaptureDevice::TakePhotoCallback still_capture_callback; + }; + + RequestManager(cros::mojom::Camera3CallbackOpsRequest callback_ops_request, + std::unique_ptr<StreamCaptureInterface> capture_interface, + CameraDeviceContext* device_context, + std::unique_ptr<CameraBufferFactory> camera_buffer_factory, + BlobifyCallback blobify_callback, + scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner); + ~RequestManager() override; + + // Sets up the stream context and allocate buffers according to the + // configuration specified in |streams|. + void SetUpStreamsAndBuffers( + VideoCaptureFormat capture_format, + const cros::mojom::CameraMetadataPtr& static_metadata, + std::vector<cros::mojom::Camera3StreamPtr> streams); + + cros::mojom::Camera3StreamPtr GetStreamConfiguration(StreamType stream_type); + + // StartPreview is the entry point to starting the video capture. The way + // the video capture loop works is: + // + // (1) Preparing capture request by mixing repeating request with still + // capture request if it exists. And build the capture request by + // RequestBuilder. + // (2) Once the capture request is built, it triggers SendCaptureRequest() to + // send the capture request and it will go back to (1) to generate next + // request. + // (3) The camera HAL returns the shutter time of a capture request through + // Notify, and the filled buffer through ProcessCaptureResult. + // (4) Once all the result metadata are collected, TrySubmitPendingBuffers() + // is passed and trigger SubmitCaptureResult() to deliver the filled + // buffer to Chrome. After the buffer is consumed by Chrome it is + // enqueued back to the free buffer queue. Goto (1) to start another + // capture loop. + // + // When TakePhoto() is called, an additional BLOB buffer is queued in step (2) + // to let the HAL fill the still capture JPEG image. When the JPEG image is + // returned in (4), it's passed to upper layer through the TakePhotoCallback. + void StartPreview(cros::mojom::CameraMetadataPtr preview_settings); + + // Stops the capture loop. After StopPreview is called |callback_ops_| is + // unbound, so no new capture request or result will be processed. It will + // also try to trigger Flush() and pass the |callback| to it. + void StopPreview(base::OnceCallback<void(int32_t)> callback); + + void TakePhoto(cros::mojom::CameraMetadataPtr settings, + VideoCaptureDevice::TakePhotoCallback callback); + + size_t GetNumberOfStreams(); + + // CaptureMetadataDispatcher implementations. + void AddResultMetadataObserver(ResultMetadataObserver* observer) override; + void RemoveResultMetadataObserver(ResultMetadataObserver* observer) override; + + // Queues a capture setting that will be send along with the earliest next + // capture request. + void SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) override; + + void SetRepeatingCaptureMetadata(cros::mojom::CameraMetadataTag tag, + cros::mojom::EntryType type, + size_t count, + std::vector<uint8_t> value) override; + + void UnsetRepeatingCaptureMetadata( + cros::mojom::CameraMetadataTag tag) override; + + private: + friend class RequestManagerTest; + + // Prepares a capture request by mixing repeating request with still capture + // request if it exists. + void PrepareCaptureRequest(); + + // Decorates the frame number and settings for the capture request and send it + // to HAL. + void SendCaptureRequest( + cros::mojom::Camera3CaptureRequestPtr capture_request, + VideoCaptureDevice::TakePhotoCallback take_photo_callback); + + // Callback for ProcessCaptureRequest(). + void OnProcessedCaptureRequest(int32_t result); + + // If there are some metadata set by SetCaptureMetadata() or + // SetRepeatingCaptureMetadata(), update them onto |capture_settings|. + void UpdateCaptureSettings(cros::mojom::CameraMetadataPtr* capture_settings); + + // ProcessCaptureResult receives the result metadata as well as the filled + // buffer from camera HAL. The result metadata may be divided and delivered + // in several stages. Before all the result metadata is received the + // partial results are kept in |pending_results_|. + void ProcessCaptureResult( + cros::mojom::Camera3CaptureResultPtr result) override; + + // Notify receives the shutter time of capture requests and various errors + // from camera HAL. The shutter time is used as the timestamp in the video + // frame delivered to Chrome. + void Notify(cros::mojom::Camera3NotifyMsgPtr message) override; + + void HandleNotifyError(uint32_t frame_number, + StreamType stream_type, + cros::mojom::Camera3ErrorMsgCode error_code); + + // Submits the captured buffer of frame |frame_number_| for the given + // |stream_type| to Chrome if all the required metadata and the captured + // buffer are received. After the buffer is submitted the function then + // enqueues the buffer to free buffer queue for the next capture request. + void SubmitCaptureResult(uint32_t frame_number, + StreamType stream_type, + cros::mojom::Camera3StreamBufferPtr stream_buffer); + + // Checks if the pending buffers are ready to submit. Trigger + // SubmitCaptureResult() if the buffers are ready to submit. + void TrySubmitPendingBuffers(uint32_t frame_number); + + mojo::Binding<cros::mojom::Camera3CallbackOps> callback_ops_; + + std::unique_ptr<StreamCaptureInterface> capture_interface_; + + CameraDeviceContext* device_context_; + + // StreamBufferManager should be declared before RequestBuilder since + // RequestBuilder holds an instance of StreamBufferManager and should be + // destroyed first. + std::unique_ptr<StreamBufferManager> stream_buffer_manager_; + + std::unique_ptr<RequestBuilder> request_builder_; + + BlobifyCallback blobify_callback_; + + // Where all the Mojo IPC calls takes place. + const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; + + // A flag indicating whether the capture loops is running. + bool capturing_; + + // The frame number. Increased by one for each capture request sent; reset + // to zero in AllocateAndStart. + uint32_t frame_number_; + + // The number of partial stages. |partial_result_count_| is learned by + // querying |static_metadata_|. In case the result count is absent in + // |static_metadata_|, it defaults to one which means all the result + // metadata and captured buffer of a frame are returned together in one + // shot. + uint32_t partial_result_count_; + + // The shutter time of the first frame. We derive the |timestamp| of a + // frame using the difference between the frame's shutter time and + // |first_frame_shutter_time_|. + base::TimeTicks first_frame_shutter_time_; + + // The repeating request settings. The settings come from the default preview + // request settings reported by the HAL. |repeating_request_settings_| is the + // default settings for each capture request. + cros::mojom::CameraMetadataPtr repeating_request_settings_; + + // A queue of oneshot request settings. These are the request settings for + // each still capture requests. |oneshot_request_settings_| overrides + // |repeating_request_settings_| if present. + std::queue<cros::mojom::CameraMetadataPtr> oneshot_request_settings_; + + // StreamBufferManager does not own the ResultMetadataObservers. The + // observers are responsible for removing itself before self-destruction. + std::unordered_set<ResultMetadataObserver*> result_metadata_observers_; + + // The list of settings to set/override once in the capture request. + std::vector<cros::mojom::CameraMetadataEntryPtr> capture_settings_override_; + + // The settings to set/override repeatedly in the capture request. In + // conflict with |capture_settings_override_|, this one has lower priority. + std::map<cros::mojom::CameraMetadataTag, cros::mojom::CameraMetadataEntryPtr> + capture_settings_repeating_override_; + + // Stores the pending capture results of the current in-flight frames. + std::map<uint32_t, CaptureResult> pending_results_; + + // Callback for TakePhoto(). When preparing capture request, the callback will + // be popped and moved to CaptureResult. + std::queue<VideoCaptureDevice::TakePhotoCallback> take_photo_callback_queue_; + + // Map for retrieving the last received frame number. It is used to check for + // duplicate or out of order of frames. + std::map<StreamType, uint32_t> last_received_frame_number_map_; + + base::WeakPtrFactory<RequestManager> weak_ptr_factory_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RequestManager); +}; + +} // namespace media + +#endif // MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_
diff --git a/media/capture/video/chromeos/stream_buffer_manager_unittest.cc b/media/capture/video/chromeos/request_manager_unittest.cc similarity index 71% rename from media/capture/video/chromeos/stream_buffer_manager_unittest.cc rename to media/capture/video/chromeos/request_manager_unittest.cc index 823f210..f79620d 100644 --- a/media/capture/video/chromeos/stream_buffer_manager_unittest.cc +++ b/media/capture/video/chromeos/request_manager_unittest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "media/capture/video/chromeos/request_manager.h" #include "media/capture/video/chromeos/stream_buffer_manager.h" #include <map> @@ -36,27 +37,6 @@ class MockStreamCaptureInterface : public StreamCaptureInterface { public: - void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - std::vector<StreamCaptureInterface::Plane> planes, - base::OnceCallback<void(int32_t)> callback) { - DoRegisterBuffer(buffer_id, type, drm_format, hal_pixel_format, width, - height, planes, callback); - } - MOCK_METHOD8(DoRegisterBuffer, - void(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - const std::vector<StreamCaptureInterface::Plane>& planes, - base::OnceCallback<void(int32_t)>& callback)); - void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr request, base::OnceCallback<void(int32_t)> callback) { DoProcessCaptureRequest(request, callback); @@ -101,7 +81,7 @@ } // namespace -class StreamBufferManagerTest : public ::testing::Test { +class RequestManagerTest : public ::testing::Test { public: void SetUp() override { quit_ = false; @@ -110,7 +90,7 @@ device_context_ = std::make_unique<CameraDeviceContext>( std::make_unique<unittest_internal::MockVideoCaptureClient>()); - stream_buffer_manager_ = std::make_unique<StreamBufferManager>( + request_manager_ = std::make_unique<RequestManager>( std::move(callback_ops_request), std::make_unique<MockStreamCaptureInterface>(), device_context_.get(), std::make_unique<FakeCameraBufferFactory>(), @@ -121,7 +101,7 @@ base::ThreadTaskRunnerHandle::Get()); } - void TearDown() override { stream_buffer_manager_.reset(); } + void TearDown() override { request_manager_.reset(); } void DoLoop() { run_loop_.reset(new base::RunLoop()); @@ -168,20 +148,6 @@ return static_metadata; } - void RegisterBuffer(uint64_t buffer_id, - cros::mojom::Camera3DeviceOps::BufferType type, - uint32_t drm_format, - cros::mojom::HalPixelFormat hal_pixel_format, - uint32_t width, - uint32_t height, - const std::vector<StreamCaptureInterface::Plane>& planes, - base::OnceCallback<void(int32_t)>& callback) { - if (quit_) { - return; - } - std::move(callback).Run(0); - } - void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { if (quit_) { @@ -189,16 +155,17 @@ } std::move(callback).Run(0); mock_callback_ops_->Notify(PrepareShutterNotifyMessage( - request->frame_number, base::TimeTicks::Now().ToInternalValue())); + request->frame_number, + (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds())); mock_callback_ops_->ProcessCaptureResult(PrepareCapturedResult( request->frame_number, cros::mojom::CameraMetadata::New(), 1, std::move(request->output_buffers))); } MockStreamCaptureInterface* GetMockCaptureInterface() { - EXPECT_NE(nullptr, stream_buffer_manager_.get()); + EXPECT_NE(nullptr, request_manager_.get()); return reinterpret_cast<MockStreamCaptureInterface*>( - stream_buffer_manager_->capture_interface_.get()); + request_manager_->capture_interface_.get()); } unittest_internal::MockVideoCaptureClient* GetMockVideoCaptureClient() { @@ -207,9 +174,9 @@ device_context_->client_.get()); } - std::map<uint32_t, StreamBufferManager::CaptureResult>& GetPendingResults() { - EXPECT_NE(nullptr, stream_buffer_manager_.get()); - return stream_buffer_manager_->pending_results_; + std::map<uint32_t, RequestManager::CaptureResult>& GetPendingResults() { + EXPECT_NE(nullptr, request_manager_.get()); + return request_manager_->pending_results_; } std::vector<cros::mojom::Camera3StreamPtr> PrepareCaptureStream( @@ -293,7 +260,7 @@ } protected: - std::unique_ptr<StreamBufferManager> stream_buffer_manager_; + std::unique_ptr<RequestManager> request_manager_; cros::mojom::Camera3CallbackOpsPtr mock_callback_ops_; std::unique_ptr<CameraDeviceContext> device_context_; cros::mojom::Camera3StreamPtr stream; @@ -305,35 +272,28 @@ }; // A basic sanity test to capture one frame with the capture loop. -TEST_F(StreamBufferManagerTest, SimpleCaptureTest) { +TEST_F(RequestManagerTest, SimpleCaptureTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - &StreamBufferManagerTest::QuitCaptureLoop, base::Unretained(this))); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(1)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); + &RequestManagerTest::QuitCaptureLoop, base::Unretained(this))); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) - .Times(1) - .WillOnce(Invoke(this, &StreamBufferManagerTest::ProcessCaptureRequest)); + .Times(AtLeast(1)) + .WillOnce(Invoke(this, &RequestManagerTest::ProcessCaptureRequest)); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 1), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until a captured frame is received by MockVideoCaptureClient. DoLoop(); } -// Test that the StreamBufferManager submits a captured result only after all +// Test that the RequestManager submits a captured result only after all // partial metadata are received. -TEST_F(StreamBufferManagerTest, PartialResultTest) { +TEST_F(RequestManagerTest, PartialResultTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - [](StreamBufferManagerTest* test) { + [](RequestManagerTest* test) { EXPECT_EQ(1u, test->GetPendingResults().size()); // Make sure all the three partial metadata are received before the // captured result is submitted. @@ -342,20 +302,14 @@ test->QuitCaptureLoop(); }, base::Unretained(this))); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(1)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) - .Times(1) + .Times(AtLeast(1)) .WillOnce(Invoke([this](cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { std::move(callback).Run(0); mock_callback_ops_->Notify(PrepareShutterNotifyMessage( - request->frame_number, base::TimeTicks::Now().ToInternalValue())); + request->frame_number, + (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds())); mock_callback_ops_->ProcessCaptureResult(PrepareCapturedResult( request->frame_number, cros::mojom::CameraMetadata::New(), 1, std::move(request->output_buffers))); @@ -367,11 +321,11 @@ std::vector<cros::mojom::Camera3StreamBufferPtr>())); })); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 3), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until a captured frame is received by MockVideoCaptureClient. DoLoop(); @@ -379,24 +333,16 @@ // Test that the capture loop is stopped and no frame is submitted when a device // error happens. -TEST_F(StreamBufferManagerTest, DeviceErrorTest) { +TEST_F(RequestManagerTest, DeviceErrorTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - [](StreamBufferManagerTest* test) { + [](RequestManagerTest* test) { ADD_FAILURE() << "No frame should be submitted"; test->QuitCaptureLoop(); }, base::Unretained(this))); EXPECT_CALL(*GetMockVideoCaptureClient(), OnError(_, _, _)) .Times(1) - .WillOnce( - InvokeWithoutArgs(this, &StreamBufferManagerTest::QuitCaptureLoop)); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(1) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); + .WillOnce(InvokeWithoutArgs(this, &RequestManagerTest::QuitCaptureLoop)); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) .Times(1) .WillOnce(Invoke([this](cros::mojom::Camera3CaptureRequestPtr& request, @@ -407,11 +353,11 @@ cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_DEVICE)); })); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 1), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until the MockVideoCaptureClient is deleted. DoLoop(); @@ -419,9 +365,9 @@ // Test that upon request error the erroneous frame is dropped, and the capture // loop continues. -TEST_F(StreamBufferManagerTest, RequestErrorTest) { +TEST_F(RequestManagerTest, RequestErrorTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - [](StreamBufferManagerTest* test) { + [](RequestManagerTest* test) { // Frame 0 should be dropped, and the frame callback should be called // with frame 1. EXPECT_EQ(test->GetPendingResults().end(), @@ -431,16 +377,8 @@ test->QuitCaptureLoop(); }, base::Unretained(this))); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(2)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) - .Times(2) + .Times(AtLeast(2)) .WillOnce(Invoke([this](cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { std::move(callback).Run(0); @@ -453,13 +391,13 @@ request->frame_number, cros::mojom::CameraMetadata::New(), 1, std::move(request->output_buffers))); })) - .WillOnce(Invoke(this, &StreamBufferManagerTest::ProcessCaptureRequest)); + .WillOnce(Invoke(this, &RequestManagerTest::ProcessCaptureRequest)); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 1), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until the MockVideoCaptureClient is deleted. DoLoop(); @@ -467,46 +405,40 @@ // Test that upon result error the captured buffer is submitted despite of the // missing result metadata, and the capture loop continues. -TEST_F(StreamBufferManagerTest, ResultErrorTest) { +TEST_F(RequestManagerTest, ResultErrorTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - [](StreamBufferManagerTest* test) { + [](RequestManagerTest* test) { // Frame 0 should be submitted. EXPECT_NE(test->GetPendingResults().end(), test->GetPendingResults().find(0)); test->QuitCaptureLoop(); }, base::Unretained(this))); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(1)) - .WillRepeatedly(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) .Times(AtLeast(1)) .WillOnce(Invoke([this](cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { std::move(callback).Run(0); mock_callback_ops_->Notify(PrepareShutterNotifyMessage( - request->frame_number, base::TimeTicks::Now().ToInternalValue())); + request->frame_number, + (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds())); mock_callback_ops_->ProcessCaptureResult(PrepareCapturedResult( request->frame_number, cros::mojom::CameraMetadata::New(), 1, std::move(request->output_buffers))); // Send a result error notify without sending the second partial result. - // StreamBufferManager should submit the buffer when it receives the + // RequestManager should submit the buffer when it receives the // result error. mock_callback_ops_->Notify(PrepareErrorNotifyMessage( request->frame_number, cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_RESULT)); })) - .WillOnce(Invoke(this, &StreamBufferManagerTest::ProcessCaptureRequest)); + .WillOnce(Invoke(this, &RequestManagerTest::ProcessCaptureRequest)); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 2), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until the MockVideoCaptureClient is deleted. DoLoop(); @@ -514,9 +446,9 @@ // Test that upon buffer error the erroneous buffer is dropped, and the capture // loop continues. -TEST_F(StreamBufferManagerTest, BufferErrorTest) { +TEST_F(RequestManagerTest, BufferErrorTest) { GetMockVideoCaptureClient()->SetFrameCb(base::BindOnce( - [](StreamBufferManagerTest* test) { + [](RequestManagerTest* test) { // Frame 0 should be dropped, and the frame callback should be called // with frame 1. EXPECT_EQ(test->GetPendingResults().end(), @@ -526,21 +458,14 @@ test->QuitCaptureLoop(); }, base::Unretained(this))); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(2)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) - .Times(2) + .Times(AtLeast(2)) .WillOnce(Invoke([this](cros::mojom::Camera3CaptureRequestPtr& request, base::OnceCallback<void(int32_t)>& callback) { std::move(callback).Run(0); mock_callback_ops_->Notify(PrepareShutterNotifyMessage( - request->frame_number, base::TimeTicks::Now().ToInternalValue())); + request->frame_number, + (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds())); mock_callback_ops_->Notify(PrepareErrorNotifyMessage( request->frame_number, cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_BUFFER)); @@ -550,47 +475,32 @@ request->frame_number, cros::mojom::CameraMetadata::New(), 1, std::move(request->output_buffers))); })) - .WillOnce(Invoke(this, &StreamBufferManagerTest::ProcessCaptureRequest)); + .WillOnce(Invoke(this, &RequestManagerTest::ProcessCaptureRequest)); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 1), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); // Wait until the MockVideoCaptureClient is deleted. DoLoop(); } // Test that preview and still capture buffers can be correctly submitted. -TEST_F(StreamBufferManagerTest, TakePhotoTest) { - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kPreview, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(AtLeast(1)) - .WillRepeatedly(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); - EXPECT_CALL( - *GetMockCaptureInterface(), - DoRegisterBuffer( - StreamBufferManager::GetBufferIpcId(StreamType::kStillCapture, 0), - cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, _, _, _, _, _, _)) - .Times(1) - .WillOnce(Invoke(this, &StreamBufferManagerTest::RegisterBuffer)); +TEST_F(RequestManagerTest, TakePhotoTest) { EXPECT_CALL(*GetMockCaptureInterface(), DoProcessCaptureRequest(_, _)) .Times(AtLeast(1)) - .WillRepeatedly( - Invoke(this, &StreamBufferManagerTest::ProcessCaptureRequest)); + .WillRepeatedly(Invoke(this, &RequestManagerTest::ProcessCaptureRequest)); - stream_buffer_manager_->SetUpStreamsAndBuffers( + request_manager_->SetUpStreamsAndBuffers( kDefaultCaptureFormat, GetFakeStaticMetadata(/* partial_result_count */ 1), PrepareCaptureStream(/* max_buffers */ 1)); - stream_buffer_manager_->StartPreview(cros::mojom::CameraMetadata::New()); - stream_buffer_manager_->TakePhoto( + request_manager_->StartPreview(cros::mojom::CameraMetadata::New()); + request_manager_->TakePhoto( GetFakeStaticMetadata(/* partial_result_count */ 1), - base::BindOnce([](StreamBufferManagerTest* test, + base::BindOnce([](RequestManagerTest* test, mojom::BlobPtr blob) { test->QuitCaptureLoop(); }, base::Unretained(this)));
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc index 29d0809..4420ec9 100644 --- a/media/capture/video/chromeos/stream_buffer_manager.cc +++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -4,8 +4,8 @@ #include "media/capture/video/chromeos/stream_buffer_manager.h" -#include <sync/sync.h> #include <memory> +#include <string> #include "base/bind.h" #include "base/posix/safe_strerror.h" @@ -13,59 +13,20 @@ #include "media/capture/video/chromeos/camera_buffer_factory.h" #include "media/capture/video/chromeos/camera_device_context.h" #include "media/capture/video/chromeos/camera_metadata_utils.h" +#include "media/capture/video/chromeos/request_builder.h" #include "mojo/public/cpp/platform/platform_handle.h" #include "mojo/public/cpp/system/platform_handle.h" namespace media { -namespace { - -size_t GetBufferIndex(uint64_t buffer_id) { - return buffer_id & 0xFFFFFFFF; -} - -StreamType StreamIdToStreamType(uint64_t stream_id) { - switch (stream_id) { - case 0: - return StreamType::kPreview; - case 1: - return StreamType::kStillCapture; - default: - return StreamType::kUnknown; - } -} - -} // namespace - StreamBufferManager::StreamBufferManager( - cros::mojom::Camera3CallbackOpsRequest callback_ops_request, - std::unique_ptr<StreamCaptureInterface> capture_interface, CameraDeviceContext* device_context, - std::unique_ptr<CameraBufferFactory> camera_buffer_factory, - base::RepeatingCallback< - mojom::BlobPtr(const uint8_t* buffer, - const uint32_t bytesused, - const VideoCaptureFormat& capture_format, - int screen_rotation)> blobify_callback, - scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) - : callback_ops_(this, std::move(callback_ops_request)), - capture_interface_(std::move(capture_interface)), - device_context_(device_context), + std::unique_ptr<CameraBufferFactory> camera_buffer_factory) + : device_context_(device_context), camera_buffer_factory_(std::move(camera_buffer_factory)), - blobify_callback_(std::move(blobify_callback)), - ipc_task_runner_(std::move(ipc_task_runner)), - capturing_(false), - frame_number_(0), - partial_result_count_(1), - first_frame_shutter_time_(base::TimeTicks()), - weak_ptr_factory_(this) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(callback_ops_.is_bound()); - DCHECK(device_context_); -} + weak_ptr_factory_(this) {} StreamBufferManager::~StreamBufferManager() { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); for (const auto& iter : stream_context_) { if (iter.second) { for (const auto& buf : iter.second->buffers) { @@ -77,24 +38,39 @@ } } +gfx::GpuMemoryBuffer* StreamBufferManager::GetBufferById(StreamType stream_type, + uint64_t buffer_id) { + size_t buffer_index = GetBufferIndex(buffer_id); + if (buffer_index >= stream_context_[stream_type]->buffers.size()) { + LOG(ERROR) << "Invalid buffer index: " << buffer_index + << " for stream: " << stream_type; + return nullptr; + } + return stream_context_[stream_type]->buffers[buffer_index].get(); +} + +VideoCaptureFormat StreamBufferManager::GetStreamCaptureFormat( + StreamType stream_type) { + return stream_context_[stream_type]->capture_format; +} + +bool StreamBufferManager::HasFreeBuffers( + const std::set<StreamType>& stream_types) { + for (auto stream_type : stream_types) { + if (stream_context_[stream_type]->free_buffers.empty()) { + return false; + } + } + return true; +} + void StreamBufferManager::SetUpStreamsAndBuffers( VideoCaptureFormat capture_format, const cros::mojom::CameraMetadataPtr& static_metadata, std::vector<cros::mojom::Camera3StreamPtr> streams) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - // The partial result count metadata is optional; defaults to 1 in case it - // is not set in the static metadata. - const cros::mojom::CameraMetadataEntryPtr* partial_count = GetMetadataEntry( - static_metadata, - cros::mojom::CameraMetadataTag::ANDROID_REQUEST_PARTIAL_RESULT_COUNT); - if (partial_count) { - partial_result_count_ = - *reinterpret_cast<int32_t*>((*partial_count)->data.data()); - } - for (auto& stream : streams) { DVLOG(2) << "Stream " << stream->id + << " stream_type: " << stream->stream_type << " configured: usage=" << stream->usage << " max_buffers=" << stream->max_buffers; @@ -113,14 +89,7 @@ // A better way to tell the stream type here would be to check on the usage // flags of the stream. - StreamType stream_type; - if (stream->format == - cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_YCbCr_420_888) { - stream_type = StreamType::kPreview; - } else { // stream->format == - // cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB - stream_type = StreamType::kStillCapture; - } + StreamType stream_type = StreamIdToStreamType(stream->id); stream_context_[stream_type] = std::make_unique<StreamContext>(); stream_context_[stream_type]->capture_format = capture_format; stream_context_[stream_type]->stream = std::move(stream); @@ -135,16 +104,25 @@ size_t num_buffers = stream_context_[stream_type]->stream->max_buffers; stream_context_[stream_type]->buffers.resize(num_buffers); int32_t buffer_width, buffer_height; - if (stream_type == StreamType::kPreview) { - buffer_width = stream_context_[stream_type]->stream->width; - buffer_height = stream_context_[stream_type]->stream->height; - } else { // StreamType::kStillCapture - const cros::mojom::CameraMetadataEntryPtr* jpeg_max_size = - GetMetadataEntry( - static_metadata, - cros::mojom::CameraMetadataTag::ANDROID_JPEG_MAX_SIZE); - buffer_width = *reinterpret_cast<int32_t*>((*jpeg_max_size)->data.data()); - buffer_height = 1; + switch (stream_type) { + case StreamType::kPreview: { + buffer_width = stream_context_[stream_type]->stream->width; + buffer_height = stream_context_[stream_type]->stream->height; + break; + } + case StreamType::kStillCapture: { + const cros::mojom::CameraMetadataEntryPtr* jpeg_max_size = + GetMetadataEntry( + static_metadata, + cros::mojom::CameraMetadataTag::ANDROID_JPEG_MAX_SIZE); + buffer_width = + *reinterpret_cast<int32_t*>((*jpeg_max_size)->data.data()); + buffer_height = 1; + break; + } + default: { + NOTREACHED(); + } } for (size_t j = 0; j < num_buffers; ++j) { auto buffer = camera_buffer_factory_->CreateGpuMemoryBuffer( @@ -173,30 +151,6 @@ } } -void StreamBufferManager::StartPreview( - cros::mojom::CameraMetadataPtr preview_settings) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(stream_context_[StreamType::kPreview]); - DCHECK(repeating_request_settings_.is_null()); - - capturing_ = true; - repeating_request_settings_ = std::move(preview_settings); - // We cannot use a loop to register all the free buffers in one shot here - // because the camera HAL v3 API specifies that the client cannot call - // ProcessCaptureRequest before the previous one returns. - RegisterBuffer(StreamType::kPreview); -} - -void StreamBufferManager::StopPreview( - base::OnceCallback<void(int32_t)> callback) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - capturing_ = false; - repeating_request_settings_.reset(); - if (callback) { - capture_interface_->Flush(std::move(callback)); - } -} - cros::mojom::Camera3StreamPtr StreamBufferManager::GetStreamConfiguration( StreamType stream_type) { if (!stream_context_.count(stream_type)) { @@ -205,92 +159,39 @@ return stream_context_[stream_type]->stream.Clone(); } -void StreamBufferManager::TakePhoto( - cros::mojom::CameraMetadataPtr settings, - VideoCaptureDevice::TakePhotoCallback callback) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(stream_context_[StreamType::kStillCapture]); - - still_capture_callbacks_yet_to_be_processed_.push(std::move(callback)); - - std::vector<uint8_t> frame_orientation(sizeof(int32_t)); - *reinterpret_cast<int32_t*>(frame_orientation.data()) = - base::checked_cast<int32_t>(device_context_->GetCameraFrameOrientation()); - cros::mojom::CameraMetadataEntryPtr e = - cros::mojom::CameraMetadataEntry::New(); - e->tag = cros::mojom::CameraMetadataTag::ANDROID_JPEG_ORIENTATION; - e->type = cros::mojom::EntryType::TYPE_INT32; - e->count = 1; - e->data = std::move(frame_orientation); - AddOrUpdateMetadataEntry(&settings, std::move(e)); - - oneshot_request_settings_.push(std::move(settings)); - RegisterBuffer(StreamType::kStillCapture); -} - -size_t StreamBufferManager::GetStreamNumber() { - return stream_context_.size(); -} - -void StreamBufferManager::AddResultMetadataObserver( - ResultMetadataObserver* observer) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(!result_metadata_observers_.count(observer)); - - result_metadata_observers_.insert(observer); -} - -void StreamBufferManager::RemoveResultMetadataObserver( - ResultMetadataObserver* observer) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(result_metadata_observers_.count(observer)); - - result_metadata_observers_.erase(observer); -} - -void StreamBufferManager::SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - cros::mojom::CameraMetadataEntryPtr setting = - cros::mojom::CameraMetadataEntry::New(); - - setting->tag = tag; - setting->type = type; - setting->count = count; - setting->data = std::move(value); - - capture_settings_override_.push_back(std::move(setting)); -} - -void StreamBufferManager::SetRepeatingCaptureMetadata( - cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - cros::mojom::CameraMetadataEntryPtr setting = - cros::mojom::CameraMetadataEntry::New(); - - setting->tag = tag; - setting->type = type; - setting->count = count; - setting->data = std::move(value); - - capture_settings_repeating_override_[tag] = std::move(setting); -} - -void StreamBufferManager::UnsetRepeatingCaptureMetadata( - cros::mojom::CameraMetadataTag tag) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - auto it = capture_settings_repeating_override_.find(tag); - if (it == capture_settings_repeating_override_.end()) { - LOG(ERROR) << "Unset a non-existent metadata: " << tag; - return; +base::Optional<BufferInfo> StreamBufferManager::RequestBuffer( + StreamType stream_type) { + VideoPixelFormat buffer_format = + stream_context_[stream_type]->capture_format.pixel_format; + uint32_t drm_format = PixFormatVideoToDrm(buffer_format); + if (!drm_format) { + device_context_->SetErrorState( + media::VideoCaptureError:: + kCrosHalV3BufferManagerUnsupportedVideoPixelFormat, + FROM_HERE, + std::string("Unsupported video pixel format") + + VideoPixelFormatToString(buffer_format)); + return {}; } - capture_settings_repeating_override_.erase(it); + + BufferInfo buffer_info; + buffer_info.id = stream_context_[stream_type]->free_buffers.front(); + stream_context_[stream_type]->free_buffers.pop(); + buffer_info.gpu_memory_buffer = stream_context_[stream_type] + ->buffers[GetBufferIndex(buffer_info.id)] + .get(); + buffer_info.hal_pixel_format = stream_context_[stream_type]->stream->format; + buffer_info.drm_format = drm_format; + return buffer_info; +} + +void StreamBufferManager::ReleaseBuffer(StreamType stream_type, + uint64_t buffer_id) { + stream_context_[stream_type]->free_buffers.push(buffer_id); +} + +size_t StreamBufferManager::GetNumberOfStreams() { + return stream_context_.size(); } // static @@ -302,556 +203,13 @@ return id; } -void StreamBufferManager::ApplyCaptureSettings( - cros::mojom::CameraMetadataPtr* capture_settings) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (capture_settings_override_.empty() && - capture_settings_repeating_override_.empty()) { - return; - } - - for (const auto& setting : capture_settings_repeating_override_) { - AddOrUpdateMetadataEntry(capture_settings, setting.second.Clone()); - } - - for (auto& s : capture_settings_override_) { - AddOrUpdateMetadataEntry(capture_settings, std::move(s)); - } - capture_settings_override_.clear(); - SortCameraMetadata(capture_settings); -} - -void StreamBufferManager::RegisterBuffer(StreamType stream_type) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(stream_context_[stream_type]); - - if (!capturing_) { - return; - } - - if (stream_context_[stream_type]->free_buffers.empty()) { - return; - } - - uint64_t buffer_id = stream_context_[stream_type]->free_buffers.front(); - stream_context_[stream_type]->free_buffers.pop(); - const gfx::GpuMemoryBuffer* buffer = - stream_context_[stream_type]->buffers[GetBufferIndex(buffer_id)].get(); - - VideoPixelFormat buffer_format = - stream_context_[stream_type]->capture_format.pixel_format; - uint32_t drm_format = PixFormatVideoToDrm(buffer_format); - if (!drm_format) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerUnsupportedVideoPixelFormat, - FROM_HERE, - std::string("Unsupported video pixel format") + - VideoPixelFormatToString(buffer_format)); - return; - } - cros::mojom::HalPixelFormat hal_pixel_format = - stream_context_[stream_type]->stream->format; - - gfx::NativePixmapHandle buffer_handle = - buffer->CloneHandle().native_pixmap_handle; - - size_t num_planes = buffer_handle.planes.size(); - DCHECK_EQ(num_planes, buffer_handle.fds.size()); - // Take ownership of fds. - std::vector<base::ScopedFD> fds(num_planes); - for (size_t i = 0; i < num_planes; ++i) - fds[i] = base::ScopedFD(buffer_handle.fds[i].fd); - - std::vector<StreamCaptureInterface::Plane> planes(num_planes); - for (size_t i = 0; i < num_planes; ++i) { - planes[i].fd = - mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fds[i]))); - if (!planes[i].fd.is_valid()) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerFailedToWrapGpuMemoryHandle, - FROM_HERE, "Failed to wrap gpu memory handle"); - return; - } - planes[i].stride = buffer_handle.planes[i].stride; - planes[i].offset = buffer_handle.planes[i].offset; - } - if (stream_type == StreamType::kStillCapture) { - still_capture_callbacks_currently_processing_.push( - std::move(still_capture_callbacks_yet_to_be_processed_.front())); - still_capture_callbacks_yet_to_be_processed_.pop(); - } - // We reuse BufferType::GRALLOC here since on ARC++ we are using DMA-buf-based - // gralloc buffers. - capture_interface_->RegisterBuffer( - buffer_id, cros::mojom::Camera3DeviceOps::BufferType::GRALLOC, drm_format, - hal_pixel_format, buffer->GetSize().width(), buffer->GetSize().height(), - std::move(planes), - base::BindOnce(&StreamBufferManager::OnRegisteredBuffer, - weak_ptr_factory_.GetWeakPtr(), stream_type, buffer_id)); - DVLOG(2) << "Registered buffer " << buffer_id; -} - -void StreamBufferManager::OnRegisteredBuffer(StreamType stream_type, - uint64_t buffer_id, - int32_t result) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(stream_context_[stream_type]); - - if (!capturing_) { - return; - } - if (result) { - device_context_->SetErrorState( - media::VideoCaptureError::kCrosHalV3BufferManagerFailedToRegisterBuffer, - FROM_HERE, - std::string("Failed to register buffer: ") + - base::safe_strerror(-result)); - return; - } - stream_context_[stream_type]->registered_buffers.push(buffer_id); - ProcessCaptureRequest(); -} - -void StreamBufferManager::ProcessCaptureRequest() { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(stream_context_[StreamType::kPreview]); - - cros::mojom::Camera3CaptureRequestPtr request = - cros::mojom::Camera3CaptureRequest::New(); - request->frame_number = frame_number_; - - CaptureResult& pending_result = pending_results_[frame_number_]; - - if (!stream_context_[StreamType::kPreview]->registered_buffers.empty()) { - cros::mojom::Camera3StreamBufferPtr buffer = - cros::mojom::Camera3StreamBuffer::New(); - buffer->stream_id = static_cast<uint64_t>(StreamType::kPreview); - buffer->buffer_id = - stream_context_[StreamType::kPreview]->registered_buffers.front(); - stream_context_[StreamType::kPreview]->registered_buffers.pop(); - buffer->status = cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK; - - DVLOG(2) << "Requested capture for stream " << StreamType::kPreview - << " in frame " << frame_number_; - request->settings = repeating_request_settings_.Clone(); - request->output_buffers.push_back(std::move(buffer)); - } - - if (stream_context_.count(StreamType::kStillCapture) && - !stream_context_[StreamType::kStillCapture]->registered_buffers.empty()) { - DCHECK(!still_capture_callbacks_currently_processing_.empty()); - cros::mojom::Camera3StreamBufferPtr buffer = - cros::mojom::Camera3StreamBuffer::New(); - buffer->stream_id = static_cast<uint64_t>(StreamType::kStillCapture); - buffer->buffer_id = - stream_context_[StreamType::kStillCapture]->registered_buffers.front(); - stream_context_[StreamType::kStillCapture]->registered_buffers.pop(); - buffer->status = cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK; - - DVLOG(2) << "Requested capture for stream " << StreamType::kStillCapture - << " in frame " << frame_number_; - // Use the still capture settings and override the preview ones. - request->settings = std::move(oneshot_request_settings_.front()); - oneshot_request_settings_.pop(); - pending_result.still_capture_callback = - std::move(still_capture_callbacks_currently_processing_.front()); - still_capture_callbacks_currently_processing_.pop(); - request->output_buffers.push_back(std::move(buffer)); - } - - pending_result.unsubmitted_buffer_count = request->output_buffers.size(); - - ApplyCaptureSettings(&request->settings); - capture_interface_->ProcessCaptureRequest( - std::move(request), - base::BindOnce(&StreamBufferManager::OnProcessedCaptureRequest, - weak_ptr_factory_.GetWeakPtr())); - frame_number_++; -} - -void StreamBufferManager::OnProcessedCaptureRequest(int32_t result) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (!capturing_) { - return; - } - if (result) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerProcessCaptureRequestFailed, - FROM_HERE, - std::string("Process capture request failed: ") + - base::safe_strerror(-result)); - return; - } - // Keeps the preview stream going. - RegisterBuffer(StreamType::kPreview); -} - -void StreamBufferManager::ProcessCaptureResult( - cros::mojom::Camera3CaptureResultPtr result) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (!capturing_) { - return; - } - uint32_t frame_number = result->frame_number; - // A new partial result may be created in either ProcessCaptureResult or - // Notify. - CaptureResult& pending_result = pending_results_[frame_number]; - - // |result->pending_result| is set to 0 if the capture result contains only - // the result buffer handles and no result metadata. - if (result->partial_result) { - uint32_t result_id = result->partial_result; - if (result_id > partial_result_count_) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerInvalidPendingResultId, - FROM_HERE, - std::string("Invalid pending_result id: ") + - std::to_string(result_id)); - return; - } - if (pending_result.partial_metadata_received.count(result_id)) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerReceivedDuplicatedPartialMetadata, - FROM_HERE, - std::string("Received duplicated partial metadata: ") + - std::to_string(result_id)); - return; - } - DVLOG(2) << "Received partial result " << result_id << " for frame " - << frame_number; - pending_result.partial_metadata_received.insert(result_id); - MergeMetadata(&pending_result.metadata, result->result); - } - - if (result->output_buffers) { - if (result->output_buffers->size() > kMaxConfiguredStreams) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerIncorrectNumberOfOutputBuffersReceived, - FROM_HERE, - std::string("Incorrect number of output buffers received: ") + - std::to_string(result->output_buffers->size())); - return; - } - for (auto& stream_buffer : result->output_buffers.value()) { - DVLOG(2) << "Received capture result for frame " << frame_number - << " stream_id: " << stream_buffer->stream_id; - StreamType stream_type = StreamIdToStreamType(stream_buffer->stream_id); - if (stream_type == StreamType::kUnknown) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerInvalidTypeOfOutputBuffersReceived, - FROM_HERE, - std::string("Invalid type of output buffers received: ") + - std::to_string(stream_buffer->stream_id)); - return; - } - - // The camera HAL v3 API specifies that only one capture result can carry - // the result buffer for any given frame number. - if (stream_context_[stream_type]->capture_results_with_buffer.count( - frame_number)) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerReceivedMultipleResultBuffersForFrame, - FROM_HERE, - std::string("Received multiple result buffers for frame ") + - std::to_string(frame_number) + std::string(" for stream ") + - std::to_string(stream_buffer->stream_id)); - return; - } - - pending_result.buffers[stream_type] = std::move(stream_buffer); - stream_context_[stream_type]->capture_results_with_buffer[frame_number] = - &pending_result; - if (pending_result.buffers[stream_type]->status == - cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) { - // If the buffer is marked as error, its content is discarded for this - // frame. Send the buffer to the free list directly through - // SubmitCaptureResult. - SubmitCaptureResult(frame_number, stream_type); - } - } - } - - for (const auto& iter : stream_context_) { - TRACE_EVENT1("camera", "Capture Result", "frame_number", frame_number); - SubmitCaptureResultIfComplete(frame_number, iter.first); - } -} - -void StreamBufferManager::Notify(cros::mojom::Camera3NotifyMsgPtr message) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (!capturing_) { - return; - } - if (message->type == cros::mojom::Camera3MsgType::CAMERA3_MSG_ERROR) { - uint32_t frame_number = message->message->get_error()->frame_number; - uint64_t error_stream_id = message->message->get_error()->error_stream_id; - StreamType stream_type = StreamIdToStreamType(error_stream_id); - if (stream_type == StreamType::kUnknown) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerUnknownStreamInCamera3NotifyMsg, - FROM_HERE, - std::string("Unknown stream in Camera3NotifyMsg: ") + - std::to_string(error_stream_id)); - return; - } - cros::mojom::Camera3ErrorMsgCode error_code = - message->message->get_error()->error_code; - HandleNotifyError(frame_number, stream_type, error_code); - } else { // cros::mojom::Camera3MsgType::CAMERA3_MSG_SHUTTER - uint32_t frame_number = message->message->get_shutter()->frame_number; - uint64_t shutter_time = message->message->get_shutter()->timestamp; - DVLOG(2) << "Received shutter time for frame " << frame_number; - if (!shutter_time) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerReceivedInvalidShutterTime, - FROM_HERE, - std::string("Received invalid shutter time: ") + - std::to_string(shutter_time)); - return; - } - CaptureResult& pending_result = pending_results_[frame_number]; - // Shutter timestamp is in ns. - base::TimeTicks reference_time = - base::TimeTicks::FromInternalValue(shutter_time / 1000); - pending_result.reference_time = reference_time; - if (first_frame_shutter_time_.is_null()) { - // Record the shutter time of the first frame for calculating the - // timestamp. - first_frame_shutter_time_ = reference_time; - } - pending_result.timestamp = reference_time - first_frame_shutter_time_; - for (const auto& iter : stream_context_) { - SubmitCaptureResultIfComplete(frame_number, iter.first); - } - } -} - -void StreamBufferManager::HandleNotifyError( - uint32_t frame_number, - StreamType stream_type, - cros::mojom::Camera3ErrorMsgCode error_code) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - std::string warning_msg; - - switch (error_code) { - case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_DEVICE: - // Fatal error and no more frames will be produced by the device. - device_context_->SetErrorState( - media::VideoCaptureError::kCrosHalV3BufferManagerFatalDeviceError, - FROM_HERE, "Fatal device error"); - return; - - case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_REQUEST: - // An error has occurred in processing the request; the request - // specified by |frame_number| has been dropped by the camera device. - // Subsequent requests are unaffected. - // - // The HAL will call ProcessCaptureResult with the buffers' state set to - // STATUS_ERROR. The content of the buffers will be dropped and the - // buffers will be reused in SubmitCaptureResult. - warning_msg = - std::string("An error occurred while processing request for frame ") + - std::to_string(frame_number); - break; - - case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_RESULT: - // An error has occurred in producing the output metadata buffer for a - // result; the output metadata will not be available for the frame - // specified by |frame_number|. Subsequent requests are unaffected. - warning_msg = std::string( - "An error occurred while producing result " - "metadata for frame ") + - std::to_string(frame_number); - break; - - case cros::mojom::Camera3ErrorMsgCode::CAMERA3_MSG_ERROR_BUFFER: - // An error has occurred in placing the output buffer into a stream for - // a request. |frame_number| specifies the request for which the buffer - // was dropped, and |stream_type| specifies the stream that dropped - // the buffer. - // - // The HAL will call ProcessCaptureResult with the buffer's state set to - // STATUS_ERROR. The content of the buffer will be dropped and the - // buffer will be reused in SubmitCaptureResult. - warning_msg = - std::string( - "An error occurred while filling output buffer of stream ") + - StreamTypeToString(stream_type) + std::string(" in frame ") + - std::to_string(frame_number); - break; - - default: - // To eliminate the warning for not handling CAMERA3_MSG_NUM_ERRORS - break; - } - - LOG(WARNING) << warning_msg << stream_type; - device_context_->LogToClient(warning_msg); - // If the buffer is already returned by the HAL, submit it and we're done. - if (pending_results_.count(frame_number) && - pending_results_[frame_number].buffers.count(stream_type)) { - SubmitCaptureResult(frame_number, stream_type); - } -} - -void StreamBufferManager::SubmitCaptureResultIfComplete( - uint32_t frame_number, - StreamType stream_type) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - - if (!pending_results_.count(frame_number)) { - // The capture result may be discarded in case of error. - return; - } - - CaptureResult& pending_result = pending_results_[frame_number]; - if (!stream_context_[stream_type]->capture_results_with_buffer.count( - frame_number) || - *pending_result.partial_metadata_received.rbegin() < - partial_result_count_ || - pending_result.reference_time == base::TimeTicks()) { - // We can only submit the result buffer of |frame_number| for |stream_type| - // when: - // 1. The result buffer for |stream_type| is received, and - // 2. Received partial result id equals to partial result count, and - // 3. The shutter time is received. - return; - } - SubmitCaptureResult(frame_number, stream_type); -} - -void StreamBufferManager::SubmitCaptureResult(uint32_t frame_number, - StreamType stream_type) { - DCHECK(ipc_task_runner_->BelongsToCurrentThread()); - DCHECK(pending_results_.count(frame_number)); - DCHECK(stream_context_[stream_type]->capture_results_with_buffer.count( - frame_number)); - - CaptureResult& pending_result = - *stream_context_[stream_type]->capture_results_with_buffer[frame_number]; - if (stream_context_[stream_type] - ->capture_results_with_buffer.begin() - ->first != frame_number) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerReceivedFrameIsOutOfOrder, - FROM_HERE, - std::string("Received frame is out-of-order; expect ") + - std::to_string(pending_results_.begin()->first) + - std::string(" but got ") + std::to_string(frame_number)); - return; - } - - DVLOG(2) << "Submit capture result of frame " << frame_number - << " for stream " << static_cast<int>(stream_type); - for (auto* iter : result_metadata_observers_) { - iter->OnResultMetadataAvailable(pending_result.metadata); - } - - DCHECK(pending_result.buffers[stream_type]); - const cros::mojom::Camera3StreamBufferPtr& stream_buffer = - pending_result.buffers[stream_type]; - uint64_t buffer_id = stream_buffer->buffer_id; - - // Wait on release fence before delivering the result buffer to client. - if (stream_buffer->release_fence.is_valid()) { - const int kSyncWaitTimeoutMs = 1000; - mojo::PlatformHandle fence = - mojo::UnwrapPlatformHandle(std::move(stream_buffer->release_fence)); - if (!fence.is_valid()) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerFailedToUnwrapReleaseFenceFd, - FROM_HERE, "Failed to unwrap release fence fd"); - return; - } - if (!sync_wait(fence.GetFD().get(), kSyncWaitTimeoutMs)) { - device_context_->SetErrorState( - media::VideoCaptureError:: - kCrosHalV3BufferManagerSyncWaitOnReleaseFenceTimedOut, - FROM_HERE, "Sync wait on release fence timed out"); - return; - } - } - - // Deliver the captured data to client. - if (stream_buffer->status != - cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_ERROR) { - size_t buffer_index = GetBufferIndex(buffer_id); - gfx::GpuMemoryBuffer* buffer = - stream_context_[stream_type]->buffers[buffer_index].get(); - if (stream_type == StreamType::kPreview) { - device_context_->SubmitCapturedData( - buffer, stream_context_[StreamType::kPreview]->capture_format, - pending_result.reference_time, pending_result.timestamp); - ipc_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&StreamBufferManager::RegisterBuffer, - weak_ptr_factory_.GetWeakPtr(), StreamType::kPreview)); - } else { // StreamType::kStillCapture - DCHECK(pending_result.still_capture_callback); - const Camera3JpegBlob* header = reinterpret_cast<Camera3JpegBlob*>( - reinterpret_cast<uintptr_t>(buffer->memory(0)) + - buffer->GetSize().width() - sizeof(Camera3JpegBlob)); - if (header->jpeg_blob_id != kCamera3JpegBlobId) { - device_context_->SetErrorState( - media::VideoCaptureError::kCrosHalV3BufferManagerInvalidJpegBlob, - FROM_HERE, "Invalid JPEG blob"); - return; - } - // Still capture result from HALv3 already has orientation info in EXIF, - // so just provide 0 as screen rotation in |blobify_callback_| parameters. - mojom::BlobPtr blob = blobify_callback_.Run( - reinterpret_cast<uint8_t*>(buffer->memory(0)), header->jpeg_size, - stream_context_[stream_type]->capture_format, 0); - if (blob) { - std::move(pending_result.still_capture_callback).Run(std::move(blob)); - } else { - LOG(ERROR) << "Failed to blobify the captured JPEG image"; - } - } - } - - stream_context_[stream_type]->free_buffers.push(buffer_id); - stream_context_[stream_type]->capture_results_with_buffer.erase(frame_number); - pending_result.unsubmitted_buffer_count--; - if (!pending_result.unsubmitted_buffer_count) { - pending_results_.erase(frame_number); - } - - if (stream_type == StreamType::kPreview) { - // Always keep the preview stream running. - RegisterBuffer(StreamType::kPreview); - } else { // stream_type == StreamType::kStillCapture - if (!still_capture_callbacks_yet_to_be_processed_.empty()) { - RegisterBuffer(StreamType::kStillCapture); - } - } +// static +size_t StreamBufferManager::GetBufferIndex(uint64_t buffer_id) { + return buffer_id & 0xFFFFFFFF; } StreamBufferManager::StreamContext::StreamContext() = default; StreamBufferManager::StreamContext::~StreamContext() = default; -StreamBufferManager::CaptureResult::CaptureResult() - : metadata(cros::mojom::CameraMetadata::New()), - unsubmitted_buffer_count(0) {} - -StreamBufferManager::CaptureResult::~CaptureResult() = default; - } // namespace media
diff --git a/media/capture/video/chromeos/stream_buffer_manager.h b/media/capture/video/chromeos/stream_buffer_manager.h index d11f86e..a71db10 100644 --- a/media/capture/video/chromeos/stream_buffer_manager.h +++ b/media/capture/video/chromeos/stream_buffer_manager.h
@@ -5,13 +5,17 @@ #ifndef MEDIA_CAPTURE_VIDEO_CHROMEOS_STREAM_BUFFER_MANAGER_H_ #define MEDIA_CAPTURE_VIDEO_CHROMEOS_STREAM_BUFFER_MANAGER_H_ +#include <cstring> +#include <initializer_list> #include <memory> #include <queue> +#include <set> #include <unordered_map> #include <vector> #include "base/containers/queue.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "media/capture/video/chromeos/camera_device_delegate.h" #include "media/capture/video/chromeos/mojo/camera3.mojom.h" @@ -22,70 +26,33 @@ class GpuMemoryBuffer; -} // namespace base +} // namespace gfx namespace media { class CameraBufferFactory; class CameraDeviceContext; -// One stream for preview, one stream for still capture. -constexpr size_t kMaxConfiguredStreams = 2; - -// The JPEG transport header as defined by Android camera HAL v3 API. The JPEG -// transport header is at the end of the blob buffer filled by the HAL. -constexpr uint16_t kCamera3JpegBlobId = 0x00FF; -struct Camera3JpegBlob { - uint16_t jpeg_blob_id; - uint32_t jpeg_size; -}; - -class CAPTURE_EXPORT CaptureMetadataDispatcher { - public: - class ResultMetadataObserver { - public: - virtual ~ResultMetadataObserver() {} - virtual void OnResultMetadataAvailable( - const cros::mojom::CameraMetadataPtr&) = 0; - }; - - virtual ~CaptureMetadataDispatcher() {} - virtual void AddResultMetadataObserver(ResultMetadataObserver* observer) = 0; - virtual void RemoveResultMetadataObserver( - ResultMetadataObserver* observer) = 0; - virtual void SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) = 0; - virtual void SetRepeatingCaptureMetadata(cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) = 0; - virtual void UnsetRepeatingCaptureMetadata( - cros::mojom::CameraMetadataTag tag) = 0; -}; +struct BufferInfo; // StreamBufferManager is responsible for managing the buffers of the // stream. StreamBufferManager allocates buffers according to the given -// stream configuration, and circulates the buffers along with capture -// requests and results between Chrome and the camera HAL process. -class CAPTURE_EXPORT StreamBufferManager final - : public cros::mojom::Camera3CallbackOps, - public CaptureMetadataDispatcher { +// stream configuration. +class CAPTURE_EXPORT StreamBufferManager final { public: StreamBufferManager( - cros::mojom::Camera3CallbackOpsRequest callback_ops_request, - std::unique_ptr<StreamCaptureInterface> capture_interface, CameraDeviceContext* device_context, - std::unique_ptr<CameraBufferFactory> camera_buffer_factory, - base::RepeatingCallback< - mojom::BlobPtr(const uint8_t* buffer, - const uint32_t bytesused, - const VideoCaptureFormat& capture_format, - int screen_rotation)> blobify_callback, - scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner); + std::unique_ptr<CameraBufferFactory> camera_buffer_factory); + ~StreamBufferManager(); - ~StreamBufferManager() override; + gfx::GpuMemoryBuffer* GetBufferById(StreamType stream_type, + uint64_t buffer_id); + + VideoCaptureFormat GetStreamCaptureFormat(StreamType stream_type); + + // Checks if all streams are available. For output stream, it is available if + // it has free buffers. + bool HasFreeBuffers(const std::set<StreamType>& stream_types); // Sets up the stream context and allocate buffers according to the // configuration specified in |stream|. @@ -94,154 +61,22 @@ const cros::mojom::CameraMetadataPtr& static_metadata, std::vector<cros::mojom::Camera3StreamPtr> streams); - // StartPreview is the entry point to starting the video capture. The way - // the video capture loop works is: - // - // (1) If there is a free buffer, RegisterBuffer registers the buffer with - // the camera HAL. - // (2) Once the free buffer is registered, ProcessCaptureRequest is called - // to issue a capture request which will eventually fill the registered - // buffer. Goto (1) to register the remaining free buffers. - // (3) The camera HAL returns the shutter time of a capture request through - // Notify, and the filled buffer through ProcessCaptureResult. - // (4) Once all the result metadata are collected, - // SubmitCaptureResultIfComplete is called to deliver the filled buffer - // to Chrome. After the buffer is consumed by Chrome it is enqueued back - // to the free buffer queue. Goto (1) to start another capture loop. - // - // When TakePhoto() is called, an additional BLOB buffer is queued in step (2) - // to let the HAL fill the still capture JPEG image. When the JPEG image is - // returned in (4), it's passed to upper layer through the TakePhotoCallback. - void StartPreview(cros::mojom::CameraMetadataPtr preview_settings); - - // Stops the capture loop. After StopPreview is called |callback_ops_| is - // unbound, so no new capture request or result will be processed. - void StopPreview(base::OnceCallback<void(int32_t)> callback); - cros::mojom::Camera3StreamPtr GetStreamConfiguration(StreamType stream_type); - void TakePhoto(cros::mojom::CameraMetadataPtr settings, - VideoCaptureDevice::TakePhotoCallback callback); + // Requests buffer for specific stream type. + base::Optional<BufferInfo> RequestBuffer(StreamType stream_type); - size_t GetStreamNumber(); + // Releases buffer by marking it as free buffer. + void ReleaseBuffer(StreamType stream_type, uint64_t buffer_id); - // CaptureMetadataDispatcher implementations. - void AddResultMetadataObserver(ResultMetadataObserver* observer) override; - void RemoveResultMetadataObserver(ResultMetadataObserver* observer) override; + size_t GetNumberOfStreams(); - // Queues a capture setting that will be send along with the earliest next - // capture request. - void SetCaptureMetadata(cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) override; - - void SetRepeatingCaptureMetadata(cros::mojom::CameraMetadataTag tag, - cros::mojom::EntryType type, - size_t count, - std::vector<uint8_t> value) override; - - void UnsetRepeatingCaptureMetadata( - cros::mojom::CameraMetadataTag tag) override; + private: + friend class RequestManagerTest; static uint64_t GetBufferIpcId(StreamType stream_type, size_t index); - private: - friend class StreamBufferManagerTest; - - // Registers a free buffer, if any, for the give |stream_type| to the camera - // HAL. - void RegisterBuffer(StreamType stream_type); - - // Calls ProcessCaptureRequest if the buffer specified by |buffer_id| is - // successfully registered. - void OnRegisteredBuffer(StreamType stream_type, - uint64_t buffer_id, - int32_t result); - - // The capture request contains the buffer handles waiting to be filled. - void ProcessCaptureRequest(); - // Calls RegisterBuffer to attempt to register any remaining free buffers. - void OnProcessedCaptureRequest(int32_t result); - - // Camera3CallbackOps implementations. - - // ProcessCaptureResult receives the result metadata as well as the filled - // buffer from camera HAL. The result metadata may be divided and delivered - // in several stages. Before all the result metadata is received the - // partial results are kept in |pending_results_|. - void ProcessCaptureResult( - cros::mojom::Camera3CaptureResultPtr result) override; - - // Notify receives the shutter time of capture requests and various errors - // from camera HAL. The shutter time is used as the timestamp in the video - // frame delivered to Chrome. - void Notify(cros::mojom::Camera3NotifyMsgPtr message) override; - void HandleNotifyError(uint32_t frame_number, - StreamType stream_type, - cros::mojom::Camera3ErrorMsgCode error_code); - - // Submits the captured buffer of frame |frame_number_| for the give - // |stream_type| to Chrome if all the required metadata and the captured - // buffer are received. After the buffer is submitted the function then - // enqueues the buffer to free buffer queue for the next capture request. - void SubmitCaptureResultIfComplete(uint32_t frame_number, - StreamType stream_type); - void SubmitCaptureResult(uint32_t frame_number, StreamType stream_type); - - void ApplyCaptureSettings(cros::mojom::CameraMetadataPtr* capture_settings); - - mojo::Binding<cros::mojom::Camera3CallbackOps> callback_ops_; - - std::unique_ptr<StreamCaptureInterface> capture_interface_; - - CameraDeviceContext* device_context_; - - std::unique_ptr<CameraBufferFactory> camera_buffer_factory_; - - base::RepeatingCallback<mojom::BlobPtr( - const uint8_t* buffer, - const uint32_t bytesused, - const VideoCaptureFormat& capture_format, - int screen_rotation)> - blobify_callback_; - - // Where all the Mojo IPC calls takes place. - const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_; - - // A flag indicating whether the capture loops is running. - bool capturing_; - - // The frame number. Increased by one for each capture request sent; reset - // to zero in AllocateAndStart. - uint32_t frame_number_; - - // CaptureResult is used to hold the pending capture results for each frame. - struct CaptureResult { - CaptureResult(); - ~CaptureResult(); - // |reference_time| and |timestamp| are derived from the shutter time of - // this frame. They are be passed to |client_->OnIncomingCapturedData| - // along with the |buffers| when the captured frame is submitted. - base::TimeTicks reference_time; - base::TimeDelta timestamp; - // The result metadata. Contains various information about the captured - // frame. - cros::mojom::CameraMetadataPtr metadata; - // The buffer handles that hold the captured data of this frame. - std::unordered_map<StreamType, cros::mojom::Camera3StreamBufferPtr> buffers; - // The set of the partial metadata received. For each capture result, the - // total number of partial metadata should equal to - // |partial_result_count_|. - std::set<uint32_t> partial_metadata_received; - // Incremented for every stream buffer requested for the given frame. - // StreamBufferManager destructs the CaptureResult when - // |unsubmitted_buffer_count| drops to zero. - size_t unsubmitted_buffer_count; - // The callback used to return the captured still capture JPEG buffer. Set - // if and only if the capture request was sent with a still capture buffer. - VideoCaptureDevice::TakePhotoCallback still_capture_callback; - }; + static size_t GetBufferIndex(uint64_t buffer_id); struct StreamContext { StreamContext(); @@ -255,60 +90,15 @@ // The free buffers of this stream. The queue stores indices into the // |buffers| vector. std::queue<uint64_t> free_buffers; - // The buffers that are registered to the HAL, which can be used as the - // output buffers for capture requests. - std::queue<uint64_t> registered_buffers; - // The pointers to the pending capture results that have unsubmitted result - // buffers. - std::map<uint32_t, CaptureResult*> capture_results_with_buffer; }; // The context for the set of active streams. std::unordered_map<StreamType, std::unique_ptr<StreamContext>> stream_context_; - // The repeating request settings. The settings come from the default preview - // request settings reported by the HAL. |repeating_request_settings_| is the - // default settings for each capture request. - cros::mojom::CameraMetadataPtr repeating_request_settings_; + CameraDeviceContext* device_context_; - // A queue of oneshot request settings. These are the request settings for - // each still capture requests. |oneshot_request_settings_| overrides - // |repeating_request_settings_| if present. - std::queue<cros::mojom::CameraMetadataPtr> oneshot_request_settings_; - - // The pending callbacks for the TakePhoto requests. - std::queue<VideoCaptureDevice::TakePhotoCallback> - still_capture_callbacks_yet_to_be_processed_; - std::queue<VideoCaptureDevice::TakePhotoCallback> - still_capture_callbacks_currently_processing_; - - // The number of partial stages. |partial_result_count_| is learned by - // querying |static_metadata_|. In case the result count is absent in - // |static_metadata_|, it defaults to one which means all the result - // metadata and captured buffer of a frame are returned together in one - // shot. - uint32_t partial_result_count_; - - // The shutter time of the first frame. We derive the |timestamp| of a - // frame using the difference between the frame's shutter time and - // |first_frame_shutter_time_|. - base::TimeTicks first_frame_shutter_time_; - - // Stores the pending capture results of the current in-flight frames. - std::map<uint32_t, CaptureResult> pending_results_; - - // StreamBufferManager does not own the ResultMetadataObservers. The - // observers are responsible for removing itself before self-destruction. - std::unordered_set<ResultMetadataObserver*> result_metadata_observers_; - - // The list of settings to set/override once in the capture request. - std::vector<cros::mojom::CameraMetadataEntryPtr> capture_settings_override_; - - // The settings to set/override repeatedly in the capture request. In - // conflict with |capture_settings_override_|, this one has lower priority. - std::map<cros::mojom::CameraMetadataTag, cros::mojom::CameraMetadataEntryPtr> - capture_settings_repeating_override_; + std::unique_ptr<CameraBufferFactory> camera_buffer_factory_; base::WeakPtrFactory<StreamBufferManager> weak_ptr_factory_;
diff --git a/media/capture/video/mock_gpu_memory_buffer_manager.cc b/media/capture/video/mock_gpu_memory_buffer_manager.cc index 9f86633..b07328e 100644 --- a/media/capture/video/mock_gpu_memory_buffer_manager.cc +++ b/media/capture/video/mock_gpu_memory_buffer_manager.cc
@@ -9,7 +9,7 @@ #include "build/build_config.h" #if defined(OS_CHROMEOS) -#include "media/capture/video/chromeos/stream_buffer_manager.h" +#include "media/capture/video/chromeos/request_manager.h" #endif using ::testing::Return;
diff --git a/media/mojo/services/mojo_audio_output_stream.cc b/media/mojo/services/mojo_audio_output_stream.cc index 1fb18bb..27ee8f7 100644 --- a/media/mojo/services/mojo_audio_output_stream.cc +++ b/media/mojo/services/mojo_audio_output_stream.cc
@@ -11,6 +11,7 @@ #include "base/callback_helpers.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/sync_socket.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" namespace media {
diff --git a/media/mojo/services/mojo_audio_output_stream_provider.cc b/media/mojo/services/mojo_audio_output_stream_provider.cc index f7893f8..acfd83d 100644 --- a/media/mojo/services/mojo_audio_output_stream_provider.cc +++ b/media/mojo/services/mojo_audio_output_stream_provider.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "build/build_config.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/message.h" namespace media {
diff --git a/media/mojo/services/mojo_audio_output_stream_unittest.cc b/media/mojo/services/mojo_audio_output_stream_unittest.cc index 5f701d17..e5d80a12 100644 --- a/media/mojo/services/mojo_audio_output_stream_unittest.cc +++ b/media/mojo/services/mojo_audio_output_stream_unittest.cc
@@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/sync_socket.h" #include "media/audio/audio_output_controller.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/platform_handle.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/media/remoting/renderer_controller.h b/media/remoting/renderer_controller.h index 90abdda..6da6f94c 100644 --- a/media/remoting/renderer_controller.h +++ b/media/remoting/renderer_controller.h
@@ -17,6 +17,7 @@ #include "media/base/media_observer.h" #include "media/media_buildflags.h" #include "media/mojo/interfaces/remoting.mojom.h" +#include "media/mojo/interfaces/remoting_common.mojom.h" #include "media/remoting/metrics.h" #include "mojo/public/cpp/bindings/binding.h"
diff --git a/mojo/public/cpp/bindings/features.cc b/mojo/public/cpp/bindings/features.cc index 36c00d99..9026ba7 100644 --- a/mojo/public/cpp/bindings/features.cc +++ b/mojo/public/cpp/bindings/features.cc
@@ -19,10 +19,8 @@ // regress in performance due to high-priority messages seeing increased // latency. Ideally we'd address these cases by giving the affected bindings // higher-priority TaskRunners. -// -// TODO(https://crbug.com/866708): Enable this by default after M73 branch. const base::Feature kTaskPerMessage{"MojoTaskPerMessage", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; } // namespace features } // namespace mojo
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 12e67cb..b60b9260 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -31,7 +31,7 @@ {%- endmacro %} {#--- Begin #} -const char {{class_name}}::Name_[] = "{{namespace}}.{{class_name}}"; +const char {{class_name}}::Name_[] = "{{module_namespace}}.{{class_name}}"; {#--- Constants #} {%- for constant in interface.constants %}
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index e2470eb..443f5e2 100644 --- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -302,7 +302,7 @@ "interfaces": self.module.interfaces, "kinds": self.module.kinds, "module": self.module, - "namespace": self.module.namespace, + "module_namespace": self.module.namespace, "namespaces_as_array": NamespaceToArray(self.module.namespace), "structs": self.module.structs, "support_lazy_serialization": self.support_lazy_serialization,
diff --git a/services/audio/log_factory_adapter.h b/services/audio/log_factory_adapter.h index c55edef..f248897 100644 --- a/services/audio/log_factory_adapter.h +++ b/services/audio/log_factory_adapter.h
@@ -10,6 +10,7 @@ #include "base/containers/queue.h" #include "media/audio/audio_logging.h" #include "media/audio/fake_audio_log_factory.h" +#include "media/mojo/interfaces/audio_logging.mojom.h" #include "services/audio/public/mojom/log_factory_manager.mojom.h" namespace media {
diff --git a/services/audio/public/cpp/fake_stream_factory.h b/services/audio/public/cpp/fake_stream_factory.h index b5ad3415..2a661986d 100644 --- a/services/audio/public/cpp/fake_stream_factory.h +++ b/services/audio/public/cpp/fake_stream_factory.h
@@ -7,8 +7,11 @@ #include <string> +#include "media/mojo/interfaces/audio_input_stream.mojom.h" +#include "media/mojo/interfaces/audio_logging.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/interface_request.h" +#include "services/audio/public/mojom/audio_processing.mojom.h" #include "services/audio/public/mojom/stream_factory.mojom.h" namespace audio {
diff --git a/services/audio/public/cpp/input_ipc.cc b/services/audio/public/cpp/input_ipc.cc index 0347d35..e98ebb1 100644 --- a/services/audio/public/cpp/input_ipc.cc +++ b/services/audio/public/cpp/input_ipc.cc
@@ -8,7 +8,9 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" +#include "services/audio/public/mojom/audio_processing.mojom.h" #include "services/audio/public/mojom/constants.mojom.h" namespace audio {
diff --git a/services/audio/public/cpp/input_ipc.h b/services/audio/public/cpp/input_ipc.h index 6971b2d9..e20e59e 100644 --- a/services/audio/public/cpp/input_ipc.h +++ b/services/audio/public/cpp/input_ipc.h
@@ -15,6 +15,7 @@ #include "base/time/time.h" #include "media/audio/audio_input_ipc.h" #include "media/mojo/interfaces/audio_input_stream.mojom.h" +#include "media/mojo/interfaces/audio_logging.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/audio/public/mojom/stream_factory.mojom.h" #include "services/service_manager/public/cpp/connector.h"
diff --git a/services/audio/public/cpp/output_device.cc b/services/audio/public/cpp/output_device.cc index b2e85f0..7241e60 100644 --- a/services/audio/public/cpp/output_device.cc +++ b/services/audio/public/cpp/output_device.cc
@@ -10,6 +10,8 @@ #include "base/optional.h" #include "base/threading/thread_restrictions.h" #include "media/audio/audio_output_device_thread_callback.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" +#include "media/mojo/interfaces/audio_logging.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" #include "services/audio/public/mojom/constants.mojom.h"
diff --git a/services/audio/public/cpp/output_device.h b/services/audio/public/cpp/output_device.h index 277d970e..370d4c6 100644 --- a/services/audio/public/cpp/output_device.h +++ b/services/audio/public/cpp/output_device.h
@@ -10,6 +10,7 @@ #include "media/base/audio_renderer_sink.h" #include "media/mojo/interfaces/audio_output_stream.mojom.h" +#include "services/audio/public/mojom/audio_processing.mojom.h" #include "services/audio/public/mojom/stream_factory.mojom.h" #include "services/service_manager/public/cpp/connector.h"
diff --git a/services/audio/public/cpp/output_device_unittest.cc b/services/audio/public/cpp/output_device_unittest.cc index af31623..89e762e 100644 --- a/services/audio/public/cpp/output_device_unittest.cc +++ b/services/audio/public/cpp/output_device_unittest.cc
@@ -12,6 +12,7 @@ #include "build/build_config.h" #include "media/audio/audio_sync_reader.h" #include "media/base/audio_renderer_sink.h" +#include "media/mojo/interfaces/audio_data_pipe.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/platform_handle.h" #include "services/audio/public/cpp/fake_stream_factory.h"
diff --git a/services/identity/identity_manager_impl_unittest.cc b/services/identity/identity_manager_impl_unittest.cc index 7968932..28ae35c 100644 --- a/services/identity/identity_manager_impl_unittest.cc +++ b/services/identity/identity_manager_impl_unittest.cc
@@ -15,6 +15,7 @@ #include "services/identity/identity_service.h" #include "services/identity/public/cpp/account_state.h" #include "services/identity/public/cpp/scope_set.h" +#include "services/identity/public/mojom/account.mojom.h" #include "services/identity/public/mojom/constants.mojom.h" #include "services/identity/public/mojom/identity_manager.mojom.h" #include "services/service_manager/public/cpp/binder_registry.h"
diff --git a/services/media_session/media_controller_unittest.cc b/services/media_session/media_controller_unittest.cc index 9e2f0d2..c0090fe 100644 --- a/services/media_session/media_controller_unittest.cc +++ b/services/media_session/media_controller_unittest.cc
@@ -770,7 +770,8 @@ } } -TEST_F(MediaControllerTest, ActiveController_Actions_Observer_Empty) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +TEST_F(MediaControllerTest, DISABLED_ActiveController_Actions_Observer_Empty) { test::MockMediaSession media_session; media_session.EnableAction(mojom::MediaSessionAction::kPlay); media_session.SetIsControllable(true); @@ -790,7 +791,9 @@ } } -TEST_F(MediaControllerTest, ActiveController_Actions_Observer_WithInfo) { +// TODO(https://crbug.com/925868): Fix and re-enable this. +TEST_F(MediaControllerTest, + DISABLED_ActiveController_Actions_Observer_WithInfo) { test::MockMediaSession media_session; media_session.SetIsControllable(true);
diff --git a/services/media_session/public/cpp/test/test_media_controller.h b/services/media_session/public/cpp/test/test_media_controller.h index 8801a67d..af8de837 100644 --- a/services/media_session/public/cpp/test/test_media_controller.h +++ b/services/media_session/public/cpp/test/test_media_controller.h
@@ -10,6 +10,7 @@ #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/interface_ptr_set.h" #include "services/media_session/public/mojom/media_controller.mojom.h" +#include "services/media_session/public/mojom/media_session.mojom.h" namespace media_session { namespace test {
diff --git a/services/service_manager/runner/host/service_process_launcher_unittest.cc b/services/service_manager/runner/host/service_process_launcher_unittest.cc index 2528224..0b91049 100644 --- a/services/service_manager/runner/host/service_process_launcher_unittest.cc +++ b/services/service_manager/runner/host/service_process_launcher_unittest.cc
@@ -17,6 +17,7 @@ #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" #include "build/build_config.h" +#include "services/service_manager/public/mojom/service.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace service_manager {
diff --git a/services/video_capture/broadcasting_receiver.cc b/services/video_capture/broadcasting_receiver.cc index 19accba6..9babeca 100644 --- a/services/video_capture/broadcasting_receiver.cc +++ b/services/video_capture/broadcasting_receiver.cc
@@ -6,6 +6,7 @@ #include "base/bind.h" #include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace video_capture {
diff --git a/services/video_capture/broadcasting_receiver.h b/services/video_capture/broadcasting_receiver.h index 19753d1..47636de 100644 --- a/services/video_capture/broadcasting_receiver.h +++ b/services/video_capture/broadcasting_receiver.h
@@ -11,6 +11,7 @@ #include "base/sequence_checker.h" #include "media/capture/video/video_frame_receiver.h" #include "services/video_capture/public/mojom/receiver.mojom.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace video_capture {
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc index 49bcd88..ba80b1c 100644 --- a/services/video_capture/device_factory_media_to_mojo_adapter.cc +++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -14,6 +14,7 @@ #include "media/capture/video/video_capture_device_info.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/video_capture/device_media_to_mojo_adapter.h" +#include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/uma/video_capture_service_event.h" namespace {
diff --git a/services/video_capture/device_factory_provider_impl.h b/services/video_capture/device_factory_provider_impl.h index f3556e1..19b641a 100644 --- a/services/video_capture/device_factory_provider_impl.h +++ b/services/video_capture/device_factory_provider_impl.h
@@ -14,7 +14,9 @@ #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_context_ref.h" +#include "services/video_capture/public/mojom/device_factory.mojom.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +#include "services/video_capture/public/mojom/video_source_provider.mojom.h" namespace video_capture {
diff --git a/services/video_capture/public/cpp/mock_device_factory.h b/services/video_capture/public/cpp/mock_device_factory.h index 8fc641b..bfb3827 100644 --- a/services/video_capture/public/cpp/mock_device_factory.h +++ b/services/video_capture/public/cpp/mock_device_factory.h
@@ -6,6 +6,7 @@ #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_DEVICE_FACTORY_H_ #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/producer.mojom.h" #include "testing/gmock/include/gmock/gmock.h" namespace video_capture {
diff --git a/services/video_capture/public/cpp/mock_producer.h b/services/video_capture/public/cpp/mock_producer.h index 084168c..cf506da 100644 --- a/services/video_capture/public/cpp/mock_producer.h +++ b/services/video_capture/public/cpp/mock_producer.h
@@ -5,6 +5,7 @@ #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_PRODUCER_H_ #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_PRODUCER_H_ +#include "media/capture/mojom/video_capture_types.mojom.h" #include "media/mojo/interfaces/media_types.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/video_capture/public/mojom/producer.mojom.h"
diff --git a/services/video_capture/public/cpp/mock_push_subscription.h b/services/video_capture/public/cpp/mock_push_subscription.h index 5293a9e..ee7ebc8 100644 --- a/services/video_capture/public/cpp/mock_push_subscription.h +++ b/services/video_capture/public/cpp/mock_push_subscription.h
@@ -5,6 +5,7 @@ #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_PUSH_SUBSCRIPTION_H_ #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_PUSH_SUBSCRIPTION_H_ +#include "media/capture/mojom/image_capture.mojom.h" #include "services/video_capture/public/mojom/video_source.mojom.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/services/video_capture/public/cpp/mock_receiver.cc b/services/video_capture/public/cpp/mock_receiver.cc index f49123f..788ee07 100644 --- a/services/video_capture/public/cpp/mock_receiver.cc +++ b/services/video_capture/public/cpp/mock_receiver.cc
@@ -5,6 +5,7 @@ #include "services/video_capture/public/cpp/mock_receiver.h" #include "base/stl_util.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace video_capture {
diff --git a/services/video_capture/public/cpp/mock_receiver.h b/services/video_capture/public/cpp/mock_receiver.h index 6edfa1d..aa8fb08 100644 --- a/services/video_capture/public/cpp/mock_receiver.h +++ b/services/video_capture/public/cpp/mock_receiver.h
@@ -7,9 +7,11 @@ #include <vector> +#include "media/capture/mojom/video_capture_types.mojom.h" #include "media/mojo/interfaces/media_types.mojom.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/video_capture/public/mojom/receiver.mojom.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" #include "testing/gmock/include/gmock/gmock.h" namespace video_capture {
diff --git a/services/video_capture/public/cpp/mock_video_source.h b/services/video_capture/public/cpp/mock_video_source.h index 77bb5f9..d8c54248b2 100644 --- a/services/video_capture/public/cpp/mock_video_source.h +++ b/services/video_capture/public/cpp/mock_video_source.h
@@ -5,6 +5,7 @@ #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_H_ #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_H_ +#include "services/video_capture/public/mojom/receiver.mojom.h" #include "services/video_capture/public/mojom/video_source.mojom.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/services/video_capture/public/cpp/mock_video_source_provider.h b/services/video_capture/public/cpp/mock_video_source_provider.h index 6dcaed0..9b0282d 100644 --- a/services/video_capture/public/cpp/mock_video_source_provider.h +++ b/services/video_capture/public/cpp/mock_video_source_provider.h
@@ -5,6 +5,7 @@ #ifndef SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_ #define SERVICES_VIDEO_CAPTURE_PUBLIC_CPP_MOCK_VIDEO_SOURCE_PROVIDER_H_ +#include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/public/mojom/video_source_provider.mojom.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc index e5b54860..f1d7dd3b 100644 --- a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc +++ b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc
@@ -6,6 +6,7 @@ #include "media/capture/video/shared_memory_handle_provider.h" #include "mojo/public/cpp/system/platform_handle.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace {
diff --git a/services/video_capture/push_video_stream_subscription_impl.h b/services/video_capture/push_video_stream_subscription_impl.h index 74c1dac0..92cde96 100644 --- a/services/video_capture/push_video_stream_subscription_impl.h +++ b/services/video_capture/push_video_stream_subscription_impl.h
@@ -7,6 +7,7 @@ #include "mojo/public/cpp/bindings/binding.h" #include "services/video_capture/public/mojom/device.mojom.h" +#include "services/video_capture/public/mojom/receiver.mojom.h" #include "services/video_capture/public/mojom/video_source.mojom.h" namespace video_capture {
diff --git a/services/video_capture/scoped_access_permission_media_to_mojo_adapter.h b/services/video_capture/scoped_access_permission_media_to_mojo_adapter.h index 4ef3e5d..e0c4e33 100644 --- a/services/video_capture/scoped_access_permission_media_to_mojo_adapter.h +++ b/services/video_capture/scoped_access_permission_media_to_mojo_adapter.h
@@ -7,6 +7,7 @@ #include "media/capture/video/video_capture_device_client.h" #include "services/video_capture/public/mojom/receiver.mojom.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace video_capture {
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h index 0a8d57e..208179d 100644 --- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h +++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
@@ -11,6 +11,7 @@ #include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" +#include "services/video_capture/public/mojom/receiver.mojom.h" #include "services/video_capture/public/mojom/virtual_device.mojom.h" namespace video_capture {
diff --git a/services/video_capture/test/device_factory_provider_connectortest.cc b/services/video_capture/test/device_factory_provider_connectortest.cc index 638be12..104f89b 100644 --- a/services/video_capture/test/device_factory_provider_connectortest.cc +++ b/services/video_capture/test/device_factory_provider_connectortest.cc
@@ -12,6 +12,7 @@ #include "services/service_manager/public/cpp/test/test_connector_factory.h" #include "services/video_capture/public/cpp/mock_receiver.h" #include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "services/video_capture/service_impl.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/video_capture/test/device_factory_provider_test.h b/services/video_capture/test/device_factory_provider_test.h index fe3262ba..791fa80 100644 --- a/services/video_capture/test/device_factory_provider_test.h +++ b/services/video_capture/test/device_factory_provider_test.h
@@ -12,7 +12,9 @@ #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_binding.h" #include "services/service_manager/public/cpp/test/test_service_manager.h" +#include "services/video_capture/public/mojom/device_factory.mojom.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +#include "services/video_capture/public/mojom/virtual_device.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace video_capture {
diff --git a/services/video_capture/test/device_factory_provider_unittest.cc b/services/video_capture/test/device_factory_provider_unittest.cc index 4c7e0d3..a68b27a 100644 --- a/services/video_capture/test/device_factory_provider_unittest.cc +++ b/services/video_capture/test/device_factory_provider_unittest.cc
@@ -8,7 +8,9 @@ #include "mojo/public/cpp/bindings/binding.h" #include "services/video_capture/public/cpp/mock_producer.h" #include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/device_factory.mojom.h" +#include "services/video_capture/public/mojom/virtual_device.mojom.h" #include "services/video_capture/test/device_factory_provider_test.h" #include "services/video_capture/test/mock_devices_changed_observer.h"
diff --git a/services/video_capture/test/fake_device_descriptor_unittest.cc b/services/video_capture/test/fake_device_descriptor_unittest.cc index 215319b..ad5a377 100644 --- a/services/video_capture/test/fake_device_descriptor_unittest.cc +++ b/services/video_capture/test/fake_device_descriptor_unittest.cc
@@ -6,6 +6,7 @@ #include "base/bind_helpers.h" #include "base/run_loop.h" #include "services/video_capture/public/cpp/mock_receiver.h" +#include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/test/fake_device_descriptor_test.h" using testing::_;
diff --git a/services/video_capture/test/fake_device_test.h b/services/video_capture/test/fake_device_test.h index eb9dd01d..f9974e1 100644 --- a/services/video_capture/test/fake_device_test.h +++ b/services/video_capture/test/fake_device_test.h
@@ -6,6 +6,7 @@ #define SERVICES_VIDEO_CAPTURE_TEST_FAKE_DEVICE_TEST_H_ #include "media/capture/video_capture_types.h" +#include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/test/fake_device_descriptor_test.h" #include "testing/gmock/include/gmock/gmock.h"
diff --git a/services/video_capture/test/mock_device_shared_access_unittest.cc b/services/video_capture/test/mock_device_shared_access_unittest.cc index 5ef70c8f..da8e46f 100644 --- a/services/video_capture/test/mock_device_shared_access_unittest.cc +++ b/services/video_capture/test/mock_device_shared_access_unittest.cc
@@ -16,6 +16,7 @@ #include "services/video_capture/device_factory_media_to_mojo_adapter.h" #include "services/video_capture/public/cpp/mock_receiver.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" +#include "services/video_capture/public/mojom/video_source.mojom.h" #include "services/video_capture/video_source_provider_impl.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/video_capture/test/mock_device_test.h b/services/video_capture/test/mock_device_test.h index 4932f03..66055d83 100644 --- a/services/video_capture/test/mock_device_test.h +++ b/services/video_capture/test/mock_device_test.h
@@ -12,6 +12,7 @@ #include "services/service_manager/public/cpp/service_keepalive.h" #include "services/video_capture/device_factory_media_to_mojo_adapter.h" #include "services/video_capture/public/cpp/mock_receiver.h" +#include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/device_factory_provider.mojom.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.cc b/services/video_capture/texture_virtual_device_mojo_adapter.cc index 2a45c4f..dcbb225 100644 --- a/services/video_capture/texture_virtual_device_mojo_adapter.cc +++ b/services/video_capture/texture_virtual_device_mojo_adapter.cc
@@ -11,6 +11,7 @@ #include "mojo/public/cpp/bindings/callback_helpers.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "services/video_capture/public/mojom/constants.mojom.h" +#include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" namespace video_capture {
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.h b/services/video_capture/texture_virtual_device_mojo_adapter.h index 16a7de6..2c41dcc 100644 --- a/services/video_capture/texture_virtual_device_mojo_adapter.h +++ b/services/video_capture/texture_virtual_device_mojo_adapter.h
@@ -11,6 +11,7 @@ #include "services/service_manager/public/cpp/service_context_ref.h" #include "services/video_capture/public/mojom/device.mojom.h" #include "services/video_capture/public/mojom/producer.mojom.h" +#include "services/video_capture/public/mojom/receiver.mojom.h" #include "services/video_capture/public/mojom/virtual_device.mojom.h" namespace video_capture {
diff --git a/services/video_capture/video_source_impl.h b/services/video_capture/video_source_impl.h index 16e80f2..950c281 100644 --- a/services/video_capture/video_source_impl.h +++ b/services/video_capture/video_source_impl.h
@@ -12,6 +12,8 @@ #include "mojo/public/cpp/bindings/binding_set.h" #include "services/video_capture/broadcasting_receiver.h" #include "services/video_capture/device_factory_media_to_mojo_adapter.h" +#include "services/video_capture/public/mojom/device.mojom.h" +#include "services/video_capture/public/mojom/video_source.mojom.h" #include "services/video_capture/public/mojom/video_source_provider.mojom.h" namespace video_capture {
diff --git a/services/video_capture/video_source_provider_impl.cc b/services/video_capture/video_source_provider_impl.cc index e43050ba..3c395a3c 100644 --- a/services/video_capture/video_source_provider_impl.cc +++ b/services/video_capture/video_source_provider_impl.cc
@@ -5,6 +5,7 @@ #include "services/video_capture/video_source_provider_impl.h" #include "base/bind.h" +#include "services/video_capture/public/mojom/producer.mojom.h" #include "services/video_capture/video_source_impl.h" #include "services/video_capture/virtual_device_enabled_device_factory.h"
diff --git a/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc b/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc index 25fa17e..656d3d3 100644 --- a/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc +++ b/services/ws/public/mojom/ime/ime_struct_traits_unittest.cc
@@ -11,6 +11,7 @@ #include "base/strings/utf_string_conversions.h" #include "mojo/public/cpp/base/string16_mojom_traits.h" #include "mojo/public/cpp/bindings/binding_set.h" +#include "services/ws/public/mojom/ime/ime.mojom.h" #include "services/ws/public/mojom/ime/ime_struct_traits_test.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ime/composition_text.h"
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json index 4951a90d..28d934b 100644 --- a/testing/buildbot/chromium.webrtc.fyi.json +++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -75,18 +75,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -105,6 +93,23 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-14.04" + } + ] + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--gtest_filter=WebRtc*" ], "swarming": { @@ -202,18 +207,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -232,6 +225,23 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "gpu": "8086:0a2e", + "os": "Mac-10.12.6" + } + ] + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--gtest_filter=WebRtc*" ], "swarming": { @@ -329,18 +339,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -359,6 +357,23 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--gtest_filter=WebRtc*" ], "swarming": { @@ -438,18 +453,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -468,6 +471,23 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--gtest_filter=WebRtc*" ], "swarming": { @@ -547,18 +567,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -577,6 +585,23 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-8.1-SP0" + } + ] + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--gtest_filter=WebRtc*" ], "swarming": {
diff --git a/testing/buildbot/chromium.webrtc.json b/testing/buildbot/chromium.webrtc.json index 27e271c..56d32ce 100644 --- a/testing/buildbot/chromium.webrtc.json +++ b/testing/buildbot/chromium.webrtc.json
@@ -38,18 +38,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -62,6 +50,17 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--enable-logging", "--v=1", "--test-launcher-jobs=1", @@ -142,18 +141,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -166,6 +153,17 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--enable-logging", "--v=1", "--test-launcher-jobs=1", @@ -246,18 +244,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -270,6 +256,17 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--enable-logging", "--v=1", "--test-launcher-jobs=1", @@ -343,18 +340,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -367,6 +352,17 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--enable-logging", "--v=1", "--test-launcher-jobs=1", @@ -440,18 +436,6 @@ "gtest_tests": [ { "args": [ - "--gtest_filter=WebRtcApprtcBrowserTest.*", - "--run-manual", - "--test-launcher-jobs=1" - ], - "name": "browser_tests_apprtc", - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "browser_tests" - }, - { - "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter", "--run-manual", "--test-launcher-jobs=1" @@ -464,6 +448,17 @@ }, { "args": [ + "--gtest_filter=WebRtcApprtcBrowserTest.*", + "--run-manual", + "--test-launcher-jobs=1" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests_apprtc" + }, + { + "args": [ "--enable-logging", "--v=1", "--test-launcher-jobs=1",
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index f2039308..bfc4da6 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -405,6 +405,11 @@ "label": "//chrome/test:browser_tests", "type": "windowed_test_launcher", }, + "browser_tests_apprtc": { + "label": "//chrome/test:browser_tests_apprtc", + "type": "windowed_test_launcher", + "executable": "browser_tests", + }, "cacheinvalidation_unittests": { "label": "//third_party/cacheinvalidation:cacheinvalidation_unittests", "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index f43c84e..930e5a0 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -4302,11 +4302,6 @@ 'browser_tests_apprtc': { 'args': ['--gtest_filter=WebRtcApprtcBrowserTest.*', '--run-manual', '--test-launcher-jobs=1'], - 'test': 'browser_tests', - # TODO(crbug.com/888429): Port this test to swarming. - 'swarming': { - 'can_use_on_swarming_builders': False, - }, }, 'content_unittests': { 'args': [
diff --git a/third_party/blink/common/DEPS b/third_party/blink/common/DEPS index b64c82e..83711aa 100644 --- a/third_party/blink/common/DEPS +++ b/third_party/blink/common/DEPS
@@ -13,6 +13,7 @@ "+mojo", "+services/metrics/public/cpp", "+services/network/public/cpp", + "+services/network/public/mojom/url_loader.mojom.h", "+skia/public/interfaces/bitmap_skbitmap_struct_traits.h", "+testing/gmock/include/gmock", "+testing/gtest/include/gtest",
diff --git a/third_party/blink/common/loader/url_loader_factory_bundle.cc b/third_party/blink/common/loader/url_loader_factory_bundle.cc index acab90d..45b5841 100644 --- a/third_party/blink/common/loader/url_loader_factory_bundle.cc +++ b/third_party/blink/common/loader/url_loader_factory_bundle.cc
@@ -8,6 +8,7 @@ #include "base/logging.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" #include "url/gurl.h" namespace blink {
diff --git a/third_party/blink/public/mojom/payments/payment_request.mojom b/third_party/blink/public/mojom/payments/payment_request.mojom index 9135e62..2bb404f 100644 --- a/third_party/blink/public/mojom/payments/payment_request.mojom +++ b/third_party/blink/public/mojom/payments/payment_request.mojom
@@ -41,6 +41,12 @@ enum CanMakePaymentQueryResult { CAN_MAKE_PAYMENT, CANNOT_MAKE_PAYMENT, + QUERY_QUOTA_EXCEEDED, + + // Used only on localhost and file:// schemes to warn web developer that the + // query quota has been exceeded, but Chrome is returning an answer anyway. + WARNING_CAN_MAKE_PAYMENT, + WARNING_CANNOT_MAKE_PAYMENT, }; enum HasEnrolledInstrumentQueryResult { @@ -228,8 +234,9 @@ // Queries whether support for the merchant-specified payment method is // available, either because the user has a registered payment handler for // that method, or because the browser can do just-in-time registration for a - // suitable payment handler. - CanMakePayment(); + // suitable payment handler. If |legacy_mode| is true, provide the legacy + // behavior, which also checks that the user has an enrolled instrument. + CanMakePayment(bool legacy_mode); // Queries whether support for the merchant-specified payment method is // available and the user has an enrolled instrument for that payment method
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom index 3dd4665..dcd55e3 100644 --- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom +++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -18,9 +18,8 @@ const string kNavigation_ServiceWorkerSpec = "navigation:service_worker"; // S13nServiceWorker: -// Sent from the browser process to the renderer. Contains parameteres for the -// constructor of ServiceWorkerNetworkProvider used for starting a shared -// worker. +// Sent from the browser process to the renderer. Contains parameters for the +// WebServiceWorkerNetworkProvider used for starting a shared worker. struct ServiceWorkerProviderInfoForSharedWorker { int32 provider_id; associated ServiceWorkerContainerHost host_ptr_info; @@ -28,8 +27,7 @@ }; // Sent from the browser process to the renderer. Contains parameters for the -// constructor of ServiceWorkerNetworkProvider used for starting a service -// worker. +// WebServiceWorkerNetworkProvider used for starting a service worker. struct ServiceWorkerProviderInfoForStartWorker { int32 provider_id;
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom index 9725408..6a5a3ec 100644 --- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom +++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -61,7 +61,8 @@ // // TODO(leonhsl): It doesn't really need to be associated. Make it // non-associated and update - // ServiceWorkerNetworkProvider::script_loader_factory_ as well. + // WebServiceWorkerNetworkProviderImplForWorker::script_loader_factory_ + // as well. associated network.mojom.URLLoaderFactory? main_script_loader_factory, // NetworkService (PlzWorker):
diff --git a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h index b1622f2..ba68f8e 100644 --- a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h +++ b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
@@ -46,7 +46,7 @@ // thread. Currently the embedder has implementations for service worker clients // (frames and shared workers), and service workers themselves. // -// An instance of this class is owned by the associated loading context, e.g. +// Instances of this class are owned by the associated loading context, e.g. // DocumentLoader. class WebServiceWorkerNetworkProvider { public:
diff --git a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h index 732f423..5e060fd 100644 --- a/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h +++ b/third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h
@@ -78,22 +78,32 @@ // failed. virtual void FailedToFetchModuleScript() {} - // The worker script successfully loaded. Called on the main thread when the - // script is served from ResourceLoader or on the worker thread when the - // script is served via WebServiceWorkerInstalledScriptsManager. + // The worker script was successfully loaded by ResourceLoader. Called on the + // main thread. // - // This may be called before or after WorkerContextStarted(). Script - // evaluation does not start until WillEvaluateScript(). + // This is called before WorkerContextStarted(). Script evaluation does not + // start until WillEvaluateScript(). virtual void WorkerScriptLoaded() {} + // The worker script was successfully read from + // WebServiceWorkerInstalledScriptsManager. Called on the worker thread. + // + // This is called after WorkerContextStarted(). Script evaluation does not + // start until WillEvaluateScript(). + virtual void InstalledWorkerScriptLoaded() {} + // Called when a WorkerGlobalScope was created for the worker thread. This // also gives a proxy to the embedder to talk to the newly created // WorkerGlobalScope. The proxy is owned by WorkerGlobalScope and should not // be destroyed by the caller. No proxy methods should be called after // willDestroyWorkerContext() is called. // - // This may be called before or after WorkerScriptLoaded(). Script evaluation - // does not start until WillEvaluateScript(). + // For new workers (on-main-thread script fetch), this is called after + // WorkerScriptLoaded(). + // + // For installed workers, this is called before InstalledWorkerScriptLoaded(). + // + // Script evaluation does not start until WillEvaluateScript(). virtual void WorkerContextStarted(WebServiceWorkerContextProxy*) {} // Called immediately before V8 script evaluation starts for the main script.
diff --git a/third_party/blink/public/web/web_shared_worker_client.h b/third_party/blink/public/web/web_shared_worker_client.h index 88f7e32..ab767347 100644 --- a/third_party/blink/public/web/web_shared_worker_client.h +++ b/third_party/blink/public/web/web_shared_worker_client.h
@@ -64,9 +64,9 @@ WebApplicationCacheHostClient*) = 0; // Called on the main thread during initialization, before requesting the main - // script resource. Creates the ServiceWorkerNetworkProvider which is used for - // script loading (i.e., the main script and importScripts). Other requests - // (e.g., fetch and XHR) go through WebWorkerFetchContext. + // script resource. Creates the WebServiceWorkerNetworkProvider which is used + // for script loading (i.e., the main script and importScripts). Other + // requests (e.g., fetch and XHR) go through WebWorkerFetchContext. virtual std::unique_ptr<WebServiceWorkerNetworkProvider> CreateServiceWorkerNetworkProvider() = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc index bf65386a..e352fa3e 100644 --- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc +++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -140,13 +140,12 @@ script_state_->DissociateContext(); } -bool WorkerOrWorkletScriptController::InitializeContextIfNeeded( +bool WorkerOrWorkletScriptController::InitializeContext( const String& human_readable_name, const KURL& url_for_debugger) { v8::HandleScope handle_scope(isolate_); - if (IsContextInitialized()) - return true; + DCHECK(!IsContextInitialized()); // Create a new v8::Context with the worker/worklet as the global object // (aka the inner global).
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h index 57718e0f2..54f8fab 100644 --- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h +++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
@@ -71,14 +71,13 @@ // Prevents future JavaScript execution. void ForbidExecution(); - // Used by WorkerThread. Returns true if the context is successfully - // initialized or already initialized. // For WorkerGlobalScope and threaded WorkletGlobalScope, |url_for_debugger| // is and should be used only for setting name/origin that appears in // DevTools. For other global scopes, |human_readable_name| is used for // setting DOMWrapperWorld's human readable name. - bool InitializeContextIfNeeded(const String& human_readable_name, - const KURL& url_for_debugger); + // This should be called only once. + bool InitializeContext(const String& human_readable_name, + const KURL& url_for_debugger); // Used by WorkerGlobalScope: void RethrowExceptionFromImportedScript(ErrorEvent*, ExceptionState&);
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc index f01bc0f..6030414 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc +++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -34,16 +34,17 @@ FindBuffer::Results::Results(const Vector<UChar>& buffer, String search_text, - const mojom::blink::FindOptions& options) { + const blink::FindOptions options) { // We need to own the |search_text| because |text_searcher_| only has a // StringView (doesn't own the search text). search_text_ = search_text; - text_searcher_.SetPattern(search_text_, options.match_case); + text_searcher_.SetPattern(search_text_, !(options & kCaseInsensitive)); text_searcher_.SetText(buffer.data(), buffer.size()); text_searcher_.SetOffset(0); } -FindBuffer::Results::Iterator::Iterator(TextSearcherICU* text_searcher) +FindBuffer::Results::Iterator::Iterator(TextSearcherICU* text_searcher, + String search_text) : text_searcher_(text_searcher), has_match_(true) { operator++(); } @@ -63,7 +64,7 @@ if (empty_result_) return end(); text_searcher_.SetOffset(0); - return Iterator(&text_searcher_); + return Iterator(&text_searcher_, search_text_); } FindBuffer::Results::Iterator FindBuffer::Results::end() const { @@ -186,7 +187,7 @@ std::unique_ptr<FindBuffer::Results> FindBuffer::FindMatches( const WebString& search_text, - const mojom::blink::FindOptions& options) const { + const blink::FindOptions options) const { if (buffer_.IsEmpty() || search_text.length() > buffer_.size()) return std::make_unique<Results>(); String search_text_16_bit = search_text;
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.h b/third_party/blink/renderer/core/editing/finder/find_buffer.h index a6b8bc3..439627b 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer.h +++ b/third_party/blink/renderer/core/editing/finder/find_buffer.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_ #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" +#include "third_party/blink/renderer/core/editing/finder/find_options.h" #include "third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" @@ -39,13 +40,13 @@ Results(const Vector<UChar>& buffer, String search_text, - const mojom::blink::FindOptions& options); + const blink::FindOptions options); class CORE_EXPORT Iterator : public std::iterator<std::forward_iterator_tag, BufferMatchResult> { public: Iterator() = default; - Iterator(TextSearcherICU* text_searcher); + Iterator(TextSearcherICU* text_searcher, String search_text_); bool operator==(const Iterator& other) { return has_match_ == other.has_match_; @@ -78,9 +79,8 @@ }; // Finds all the match for |search_text| in |buffer_|. - std::unique_ptr<Results> FindMatches( - const WebString& search_text, - const mojom::blink::FindOptions& options) const; + std::unique_ptr<Results> FindMatches(const WebString& search_text, + const blink::FindOptions options) const; // Gets a flat tree range corresponding to text in the [start_index, // end_index) of |buffer|.
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc b/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc index 1eb23e6..32f4c47 100644 --- a/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc +++ b/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc
@@ -44,7 +44,7 @@ FindBuffer buffer(WholeDocumentRange()); EXPECT_TRUE(buffer.PositionAfterBlock().IsNull()); std::unique_ptr<FindBuffer::Results> results = - buffer.FindMatches("abce", *mojom::blink::FindOptions::New()); + buffer.FindMatches("abce", kCaseInsensitive); EXPECT_EQ(1u, results->CountForTesting()); FindBuffer::BufferMatchResult match = *results->begin(); EXPECT_EQ(0u, match.start); @@ -143,27 +143,19 @@ PositionInFlatTree end_position = PositionInFlatTree::LastPositionInNode(*node); FindBuffer buffer(EphemeralRangeInFlatTree(start_position, end_position)); - EXPECT_EQ(1u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(4u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(4u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // |start_position| = fo|ofoo // |end_position| = foof|oo start_position = PositionInFlatTree(*node, 2u); end_position = PositionInFlatTree(*node, 4u); buffer = FindBuffer(EphemeralRangeInFlatTree(start_position, end_position)); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); } TEST_F(FindBufferTest, FindBetweenPositionsDifferentNodes) { @@ -177,69 +169,45 @@ FindBuffer buffer(EphemeralRangeInFlatTree( PositionInFlatTree::FirstPositionInNode(*div->firstChild()), PositionInFlatTree::LastPositionInNode(*span->firstChild()))); - EXPECT_EQ(2u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("fo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("oof", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(4u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(3u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("fo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("oof", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(4u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(3u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>f^oo<span>foof<b>o|o</b></span></div> // So buffer = "oofoofo" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*div->firstChild(), 1), PositionInFlatTree(*b->firstChild(), 1))); - EXPECT_EQ(1u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("oof", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("fo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(5u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("oof", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("fo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(5u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>foo<span>f^oof|<b>oo</b></span></div> // So buffer = "oof" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*span->firstChild(), 1), PositionInFlatTree(*span->firstChild(), 4))); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oof", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("fo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oof", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("fo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>foo<span>foof^<b>oo|</b></span></div> // So buffer = "oo" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*span->firstChild(), 4), PositionInFlatTree(*b->firstChild(), 2))); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("oof", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("fo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("oof", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("fo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); } TEST_F(FindBufferTest, FindBetweenPositionsSkippedNodes) { @@ -258,60 +226,42 @@ FindBuffer buffer(EphemeralRangeInFlatTree( PositionInFlatTree::FirstPositionInNode(*div->firstChild()), PositionInFlatTree(*span->firstChild(), 3))); - EXPECT_EQ(1u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("oof", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("fo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("oof", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("fo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>foo<span style='display:none;'>f^oof</span><b>oo|</b> // <script>fo</script><a>o</a></div> // So buffer = "oo" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*span->firstChild(), 1), PositionInFlatTree(*b->firstChild(), 2))); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>foo<span style='display:none;'>f^oof</span><b>oo|</b> // <script>f|o</script><a>o</a></div> // So buffer = "oo" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*span->firstChild(), 1), PositionInFlatTree(*script->firstChild(), 2))); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(2u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(2u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); // <div>foo<span style='display:none;'>foof</span><b>oo|</b> // <script>f^o</script><a>o|</a></div> // So buffer = "o" buffer = FindBuffer( EphemeralRangeInFlatTree(PositionInFlatTree(*script->firstChild(), 1), PositionInFlatTree(*a->firstChild(), 1))); - EXPECT_EQ(0u, buffer.FindMatches("foo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("oo", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("o", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("f", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("foo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("oo", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("o", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting()); } class FindBufferBlockTest : public FindBufferTest, @@ -332,52 +282,38 @@ FindBuffer text_buffer(WholeDocumentRange()); EXPECT_EQ(GetElementById("block"), *text_buffer.PositionAfterBlock().ComputeContainerNode()); - EXPECT_EQ(1u, - text_buffer.FindMatches("text", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, text_buffer - .FindMatches("textblock", *mojom::blink::FindOptions::New()) - ->CountForTesting()); EXPECT_EQ( - 0u, - text_buffer.FindMatches("text block", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + 1u, text_buffer.FindMatches("text", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, text_buffer.FindMatches("textblock", kCaseInsensitive) + ->CountForTesting()); + EXPECT_EQ(0u, text_buffer.FindMatches("text block", kCaseInsensitive) + ->CountForTesting()); FindBuffer block_buffer(EphemeralRangeInFlatTree( text_buffer.PositionAfterBlock(), LastPositionInDocument())); EXPECT_EQ(GetElementById("span"), *block_buffer.PositionAfterBlock().ComputeContainerNode()); EXPECT_EQ( - 1u, block_buffer.FindMatches("block", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, block_buffer - .FindMatches("textblock", *mojom::blink::FindOptions::New()) + 1u, + block_buffer.FindMatches("block", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, block_buffer.FindMatches("textblock", kCaseInsensitive) ->CountForTesting()); - EXPECT_EQ( - 0u, - block_buffer.FindMatches("text block", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, block_buffer - .FindMatches("blockspan", *mojom::blink::FindOptions::New()) + EXPECT_EQ(0u, block_buffer.FindMatches("text block", kCaseInsensitive) ->CountForTesting()); - EXPECT_EQ( - 0u, - block_buffer.FindMatches("block span", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, block_buffer.FindMatches("blockspan", kCaseInsensitive) + ->CountForTesting()); + EXPECT_EQ(0u, block_buffer.FindMatches("block span", kCaseInsensitive) + ->CountForTesting()); FindBuffer span_buffer(EphemeralRangeInFlatTree( block_buffer.PositionAfterBlock(), LastPositionInDocument())); EXPECT_TRUE(span_buffer.PositionAfterBlock().IsNull()); - EXPECT_EQ(1u, - span_buffer.FindMatches("span", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, span_buffer - .FindMatches("blockspan", *mojom::blink::FindOptions::New()) - ->CountForTesting()); EXPECT_EQ( - 0u, - span_buffer.FindMatches("block span", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + 1u, span_buffer.FindMatches("span", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, span_buffer.FindMatches("blockspan", kCaseInsensitive) + ->CountForTesting()); + EXPECT_EQ(0u, span_buffer.FindMatches("block span", kCaseInsensitive) + ->CountForTesting()); } class FindBufferSeparatorTest @@ -398,8 +334,7 @@ TEST_P(FindBufferSeparatorTest, FindSeparatedElements) { SetBodyContent("a<" + GetParam() + ">a</" + GetParam() + ">a"); FindBuffer buffer(WholeDocumentRange()); - EXPECT_EQ(0u, buffer.FindMatches("aa", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("aa", kCaseInsensitive)->CountForTesting()); } TEST_F(FindBufferTest, WhiteSpaceCollapsingPreWrap) { @@ -408,63 +343,116 @@ "</span>"); FindBuffer buffer(WholeDocumentRange()); EXPECT_EQ( - 1u, buffer.FindMatches("a b c d e ", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + 1u, + buffer.FindMatches("a b c d e ", kCaseInsensitive)->CountForTesting()); }; TEST_F(FindBufferTest, WhiteSpaceCollapsingPre) { SetBodyContent("<div style='white-space: pre;'>a \n b</div>"); FindBuffer buffer(WholeDocumentRange()); - EXPECT_EQ(1u, buffer.FindMatches("a", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("ab", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a\n b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a \nb", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a \n b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("a", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("ab", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a\n b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a \nb", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a \n b", kCaseInsensitive)->CountForTesting()); } TEST_F(FindBufferTest, WhiteSpaceCollapsingPreLine) { SetBodyContent("<div style='white-space: pre-line;'>a \n b</div>"); FindBuffer buffer(WholeDocumentRange()); - EXPECT_EQ(1u, buffer.FindMatches("a", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(1u, buffer.FindMatches("b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("ab", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a \n b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a\n b", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a \nb", *mojom::blink::FindOptions::New()) - ->CountForTesting()); - EXPECT_EQ(0u, buffer.FindMatches("a\nb", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("a", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("ab", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a \n b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a\n b", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a \nb", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, + buffer.FindMatches("a\nb", kCaseInsensitive)->CountForTesting()); } TEST_F(FindBufferTest, BidiTest) { SetBodyContent("<bdo dir=rtl id=bdo>foo<span>bar</span></bdo>"); FindBuffer buffer(WholeDocumentRange()); - EXPECT_EQ(1u, buffer.FindMatches("foobar", *mojom::blink::FindOptions::New()) - ->CountForTesting()); + EXPECT_EQ(1u, + buffer.FindMatches("foobar", kCaseInsensitive)->CountForTesting()); +} + +TEST_F(FindBufferTest, KanaSmallVsNormal) { + SetBodyContent("や"); // Normal-sized や + FindBuffer buffer(WholeDocumentRange()); + // Should find normal-sized や + EXPECT_EQ(1u, buffer.FindMatches("や", kCaseInsensitive)->CountForTesting()); + // Should not find smalll-sized ゃ + EXPECT_EQ(0u, buffer.FindMatches("ゃ", kCaseInsensitive)->CountForTesting()); +} + +TEST_F(FindBufferTest, KanaDakuten) { + SetBodyContent("びゃ"); // Hiragana bya + FindBuffer buffer(WholeDocumentRange()); + // Should find bi + EXPECT_EQ(1u, buffer.FindMatches("び", kCaseInsensitive)->CountForTesting()); + // Should find smalll-sized ゃ + EXPECT_EQ(1u, buffer.FindMatches("ゃ", kCaseInsensitive)->CountForTesting()); + // Should find bya + EXPECT_EQ(1u, + buffer.FindMatches("びゃ", kCaseInsensitive)->CountForTesting()); + // Should not find hi + EXPECT_EQ(0u, buffer.FindMatches("ひ", kCaseInsensitive)->CountForTesting()); + // Should not find pi + EXPECT_EQ(0u, buffer.FindMatches("ぴ", kCaseInsensitive)->CountForTesting()); +} + +TEST_F(FindBufferTest, KanaHalfFull) { + // Should treat hiragana, katakana, half width katakana as the same. + // hiragana ra, half width katakana ki, full width katakana na + SetBodyContent("らキナ"); + FindBuffer buffer(WholeDocumentRange()); + // Should find katakana ra + EXPECT_EQ(1u, buffer.FindMatches("ラ", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("ラ", kCaseInsensitive)->CountForTesting()); + // Should find hiragana & katakana ki + EXPECT_EQ(1u, buffer.FindMatches("き", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("キ", kCaseInsensitive)->CountForTesting()); + // Should find hiragana & katakana na + EXPECT_EQ(1u, buffer.FindMatches("な", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("ナ", kCaseInsensitive)->CountForTesting()); + // Should find whole word + EXPECT_EQ(1u, + buffer.FindMatches("らきな", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("ラキナ", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, + buffer.FindMatches("ラキナ", kCaseInsensitive)->CountForTesting()); +} + +TEST_F(FindBufferTest, KanaDecomposed) { + SetBodyContent("は ゛"); + FindBuffer buffer(WholeDocumentRange()); + EXPECT_EQ(0u, buffer.FindMatches("ば", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, + buffer.FindMatches("は ゛", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(0u, buffer.FindMatches("バ ", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, + buffer.FindMatches("ハ ゛", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, buffer.FindMatches("ハ ゙", kCaseInsensitive)->CountForTesting()); + EXPECT_EQ(1u, + buffer.FindMatches("ハ ゛", kCaseInsensitive)->CountForTesting()); } } // namespace blink
diff --git a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc index 509ee934..81e1f95 100644 --- a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc +++ b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
@@ -113,11 +113,17 @@ int match_count = 0; bool full_range_searched = false; PositionInFlatTree next_task_start_position; + + blink::FindOptions find_options = + (options_->forward ? 0 : kBackwards) | + (options_->match_case ? 0 : kCaseInsensitive) | + (options_->find_next ? 0 : kStartInSelection); + do { // Find in the whole block. FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end)); std::unique_ptr<FindBuffer::Results> match_results = - buffer.FindMatches(search_text_, *options_); + buffer.FindMatches(search_text_, find_options); for (FindBuffer::BufferMatchResult match : *match_results) { const EphemeralRangeInFlatTree ephemeral_match_range = buffer.RangeFromBufferIndex(match.start,
diff --git a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc index 69dc7ade..4d16b13 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc +++ b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.cc
@@ -30,6 +30,7 @@ #include <unicode/usearch.h> #include "base/macros.h" #include "third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h" +#include "third_party/blink/renderer/platform/text/unicode_utilities.h" #include "third_party/blink/renderer/platform/wtf/text/character_names.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -114,6 +115,10 @@ bool case_sensitive) { SetCaseSensitivity(case_sensitive); SetPattern(pattern.Characters16(), pattern.length()); + if (ContainsKanaLetters(pattern.ToString())) { + NormalizeCharactersIntoNFCForm(pattern.Characters16(), pattern.length(), + normalized_search_text_); + } } void TextSearcherICU::SetText(const UChar* text, wtf_size_t length) { @@ -130,6 +135,14 @@ } bool TextSearcherICU::NextMatchResult(MatchResultICU& result) { + while (NextMatchResultInternal(result)) { + if (!ShouldSkipCurrentMatch(result)) + return true; + } + return false; +} + +bool TextSearcherICU::NextMatchResultInternal(MatchResultICU& result) { UErrorCode status = U_ZERO_ERROR; const int match_start = usearch_next(searcher_, &status); DCHECK_EQ(status, U_ZERO_ERROR); @@ -149,6 +162,22 @@ return true; } +bool TextSearcherICU::ShouldSkipCurrentMatch(MatchResultICU& result) const { + if (normalized_search_text_.IsEmpty()) + return false; + Vector<UChar> normalized_match; + int32_t text_length; + const UChar* text = usearch_getText(searcher_, &text_length); + DCHECK_LE((int32_t)(result.start + result.length), text_length); + + NormalizeCharactersIntoNFCForm(text + result.start, result.length, + normalized_match); + + return !CheckOnlyKanaLettersInStrings( + normalized_search_text_.data(), normalized_search_text_.size(), + normalized_match.begin(), normalized_match.size()); +} + void TextSearcherICU::SetPattern(const UChar* pattern, wtf_size_t length) { UErrorCode status = U_ZERO_ERROR; usearch_setPattern(searcher_, pattern, length, &status);
diff --git a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h index a99fae2..917a8ae 100644 --- a/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h +++ b/third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h
@@ -32,9 +32,12 @@ private: void SetPattern(const UChar* pattern, wtf_size_t length); void SetCaseSensitivity(bool case_sensitive); + bool ShouldSkipCurrentMatch(MatchResultICU&) const; + bool NextMatchResultInternal(MatchResultICU&); UStringSearch* searcher_ = nullptr; wtf_size_t text_length_ = 0; + Vector<UChar> normalized_search_text_; DISALLOW_COPY_AND_ASSIGN(TextSearcherICU); };
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc index 789bf35..22362102 100644 --- a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc +++ b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
@@ -202,35 +202,20 @@ : kEntityMaskInAttributeValue); } -void MarkupFormatter::AppendQuotedURLAttributeValue( - StringBuilder& result, - const Element& element, - const Attribute& attribute) { - DCHECK(element.IsURLAttribute(attribute)) << element; - String resolved_url_string = ResolveURLIfNeeded(element, attribute.Value()); - UChar quote_char = '"'; - if (ProtocolIsJavaScript(resolved_url_string)) { - // minimal escaping for javascript urls - if (resolved_url_string.Contains('&')) - resolved_url_string.Replace('&', "&"); - - if (resolved_url_string.Contains('"')) { - if (resolved_url_string.Contains('\'')) - resolved_url_string.Replace('"', """); - else - quote_char = '\''; - } - result.Append(quote_char); - result.Append(resolved_url_string); - result.Append(quote_char); - return; +void MarkupFormatter::AppendAttribute(StringBuilder& result, + const AtomicString& prefix, + const AtomicString& local_name, + const String& value, + bool document_is_html) { + result.Append(' '); + if (!prefix.IsEmpty()) { + result.Append(prefix); + result.Append(':'); } - - // FIXME: This does not fully match other browsers. Firefox percent-escapes - // non-ASCII characters for innerHTML. - result.Append(quote_char); - AppendAttributeValue(result, resolved_url_string, false); - result.Append(quote_char); + result.Append(local_name); + result.Append("=\""); + AppendAttributeValue(result, value, document_is_html); + result.Append('"'); } void MarkupFormatter::AppendNamespace(StringBuilder& result, @@ -241,16 +226,10 @@ AtomicString found_uri = namespaces.at(lookup_key); if (!EqualIgnoringNullity(found_uri, namespace_uri)) { namespaces.Set(lookup_key, namespace_uri); - result.Append(' '); - result.Append(g_xmlns_atom.GetString()); - if (!prefix.IsEmpty()) { - result.Append(':'); - result.Append(prefix); - } - - result.Append("=\""); - AppendAttributeValue(result, namespace_uri, false); - result.Append('"'); + if (prefix.IsEmpty()) + AppendAttribute(result, g_null_atom, g_xmlns_atom, namespace_uri, false); + else + AppendAttribute(result, g_xmlns_atom, prefix, namespace_uri, false); } } @@ -354,6 +333,12 @@ const Attribute& attribute, Namespaces* namespaces) { bool document_is_html = SerializeAsHTMLDocument(element); + String value = attribute.Value(); + if (element.IsURLAttribute(attribute)) { + // FIXME: This does not fully match other browsers. Firefox percent-escapes + // non-ASCII characters for innerHTML. + value = ResolveURLIfNeeded(element, value); + } if (document_is_html) { // https://html.spec.whatwg.org/multipage/parsing.html#attribute's-serialised-name @@ -366,8 +351,8 @@ } else if (attribute.NamespaceURI() == xlink_names::kNamespaceURI) { prefixed_name.SetPrefix(g_xlink_atom); } - result.Append(' '); - result.Append(prefixed_name.ToString()); + AppendAttribute(result, prefixed_name.Prefix(), prefixed_name.LocalName(), + value, document_is_html); } else { // https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes @@ -429,26 +414,8 @@ *namespaces); } } - // 3.6. Append a " " (U+0020 SPACE) to result. - result.Append(' '); - // 3.7. If candidate prefix is not null, then append to result the - // concatenation of candidate prefix with ":" (U+003A COLON). - if (candidate_prefix) { - result.Append(candidate_prefix); - result.Append(':'); - } - // 3.9.1. The value of attr's localName; - result.Append(attribute.LocalName()); - } - - result.Append('='); - - if (element.IsURLAttribute(attribute)) { - AppendQuotedURLAttributeValue(result, element, attribute); - } else { - result.Append('"'); - AppendAttributeValue(result, attribute.Value(), document_is_html); - result.Append('"'); + AppendAttribute(result, candidate_prefix, attribute.LocalName(), value, + document_is_html); } }
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_formatter.h b/third_party/blink/renderer/core/editing/serializers/markup_formatter.h index 31693ee..78b1ca2 100644 --- a/third_party/blink/renderer/core/editing/serializers/markup_formatter.h +++ b/third_party/blink/renderer/core/editing/serializers/markup_formatter.h
@@ -73,6 +73,11 @@ public: static void AppendAttributeValue(StringBuilder&, const String&, bool); + static void AppendAttribute(StringBuilder& result, + const AtomicString& prefix, + const AtomicString& local_name, + const String& value, + bool document_is_html); static void AppendCDATASection(StringBuilder&, const String&); static void AppendCharactersReplacingEntities(StringBuilder&, const String&, @@ -114,9 +119,6 @@ private: String ResolveURLIfNeeded(const Element&, const String&) const; - void AppendQuotedURLAttributeValue(StringBuilder&, - const Element&, - const Attribute&); const EAbsoluteURLs resolve_urls_method_; SerializationType serialization_type_;
diff --git a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc index 8121738..207c5aa 100644 --- a/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc +++ b/third_party/blink/renderer/core/layout/custom/layout_worklet_global_scope.cc
@@ -31,8 +31,8 @@ pending_layout_registry); String context_name("LayoutWorklet #"); context_name.append(String::Number(global_scope_number)); - global_scope->ScriptController()->InitializeContextIfNeeded(context_name, - NullURL()); + // TODO(bashi): Handle a case where the script controller fails to initialize. + global_scope->ScriptController()->InitializeContext(context_name, NullURL()); MainThreadDebugger::Instance()->ContextCreated( global_scope->ScriptController()->GetScriptState(), global_scope->GetFrame(), global_scope->DocumentSecurityOrigin());
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc index 16a0872..12d70a4 100644 --- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc +++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader_test.cc
@@ -194,8 +194,8 @@ MakeGarbageCollected<WorkletModuleResponsesMap>()); global_scope_ = MakeGarbageCollected<WorkletGlobalScope>( std::move(creation_params), *reporting_proxy_, &GetFrame()); - global_scope_->ScriptController()->InitializeContextIfNeeded("Dummy Context", - NullURL()); + ASSERT_TRUE(global_scope_->ScriptController()->InitializeContext( + "Dummy Context", NullURL())); modulator_ = MakeGarbageCollected<ModuleScriptLoaderTestModulator>( global_scope_->ScriptController()->GetScriptState()); }
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc index 6b8065d..3db17ae 100644 --- a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc +++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
@@ -225,6 +225,48 @@ TraceWrapperV8Reference<v8::Value> extra_arg_; }; +class JavaScriptStreamStartAlgorithm : public StreamStartAlgorithm { + public: + JavaScriptStreamStartAlgorithm(v8::Isolate* isolate, + v8::Local<v8::Object> recv, + const char* method_name_for_error, + v8::Local<v8::Value> controller) + : recv_(isolate, recv), + method_name_for_error_(method_name_for_error), + controller_(isolate, controller) {} + + v8::MaybeLocal<v8::Promise> Run(ScriptState* script_state, + ExceptionState& exception_state) override { + auto* isolate = script_state->GetIsolate(); + // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink + // 3. Let startAlgorithm be the following steps: + // a. Return ? InvokeOrNoop(underlyingSink, "start", « controller »). + auto value_maybe = CallOrNoop1( + script_state, recv_.NewLocal(isolate), "start", method_name_for_error_, + controller_.NewLocal(isolate), exception_state); + if (exception_state.HadException()) { + return v8::MaybeLocal<v8::Promise>(); + } + v8::Local<v8::Value> value; + if (!value_maybe.ToLocal(&value)) { + exception_state.ThrowTypeError("internal error"); + return v8::MaybeLocal<v8::Promise>(); + } + return PromiseResolve(script_state, value); + } + + void Trace(Visitor* visitor) override { + visitor->Trace(recv_); + visitor->Trace(controller_); + StreamStartAlgorithm::Trace(visitor); + } + + private: + TraceWrapperV8Reference<v8::Object> recv_; + const char* const method_name_for_error_; + TraceWrapperV8Reference<v8::Value> controller_; +}; + } // namespace // TODO(ricea): For optimal performance, method_name should be cached as an @@ -271,6 +313,16 @@ isolate, method.As<v8::Function>(), extra_arg_local, underlying_object); } +CORE_EXPORT StreamStartAlgorithm* CreateStartAlgorithm( + ScriptState* script_state, + v8::Local<v8::Object> underlying_object, + const char* method_name_for_error, + v8::Local<v8::Value> controller) { + return MakeGarbageCollected<JavaScriptStreamStartAlgorithm>( + script_state->GetIsolate(), underlying_object, method_name_for_error, + controller); +} + CORE_EXPORT v8::MaybeLocal<v8::Value> CallOrNoop1( ScriptState* script_state, v8::Local<v8::Object> object,
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.h b/third_party/blink/renderer/core/streams/miscellaneous_operations.h index 3c1458b..3e0c42d 100644 --- a/third_party/blink/renderer/core/streams/miscellaneous_operations.h +++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.h
@@ -17,6 +17,7 @@ class ScriptState; class StrategySizeAlgorithm; class StreamAlgorithm; +class StreamStartAlgorithm; // This is slightly different than the version in the standard // https://streams.spec.whatwg.org/#create-algorithm-from-underlying-method as @@ -34,6 +35,17 @@ v8::MaybeLocal<v8::Value> extra_arg, ExceptionState&); +// Create a StreamStartAlgorithm from the "start" method on |underlying_object|. +// Unlike other algorithms, the lookup of the method on the object is done at +// execution time rather than algorithm creation time. |method_name_for_error| +// is used in exception messages. It is not copied so must remain valid until +// the algorithm is run. +CORE_EXPORT StreamStartAlgorithm* CreateStartAlgorithm( + ScriptState*, + v8::Local<v8::Object> underlying_object, + const char* method_name_for_error, + v8::Local<v8::Value> controller); + // Used in place of InvokeOrNoop in spec. Always takes 1 argument. // https://streams.spec.whatwg.org/#invoke-or-noop CORE_EXPORT v8::MaybeLocal<v8::Value> CallOrNoop1(ScriptState*,
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc b/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc index 85c688e..aaf5101 100644 --- a/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc +++ b/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc
@@ -169,6 +169,86 @@ extra_arg, 1, argv)); } +TEST(MiscellaneousOperationsTest, CreateStartAlgorithmNoMethod) { + V8TestingScope scope; + auto underlying_object = v8::Object::New(scope.GetIsolate()); + v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate()); + auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object, + "underlyingSink.start", controller); + ASSERT_TRUE(algo); + auto maybe_result = algo->Run(scope.GetScriptState(), ASSERT_NO_EXCEPTION); + ASSERT_FALSE(maybe_result.IsEmpty()); + auto result = maybe_result.ToLocalChecked(); + ASSERT_EQ(result->State(), v8::Promise::kFulfilled); + EXPECT_TRUE(result->Result()->IsUndefined()); +} + +TEST(MiscellaneousOperationsTest, CreateStartAlgorithmNullMethod) { + V8TestingScope scope; + auto underlying_object = v8::Object::New(scope.GetIsolate()); + underlying_object + ->Set(scope.GetContext(), V8String(scope.GetIsolate(), "start"), + v8::Null(scope.GetIsolate())) + .Check(); + v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate()); + auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object, + "underlyingSink.start", controller); + ASSERT_TRUE(algo); + ExceptionState exception_state(scope.GetIsolate(), + ExceptionState::kExecutionContext, "", ""); + auto maybe_result = algo->Run(scope.GetScriptState(), exception_state); + EXPECT_TRUE(exception_state.HadException()); + EXPECT_TRUE(maybe_result.IsEmpty()); +} + +TEST(MiscellaneousOperationsTest, CreateStartAlgorithmThrowingMethod) { + V8TestingScope scope; + ScriptValue underlying_value = EvalWithPrintingError(&scope, + R"(({ + start() { + throw new Error(); + } +}))"); + ASSERT_TRUE(underlying_value.IsObject()); + auto underlying_object = underlying_value.V8Value().As<v8::Object>(); + v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate()); + auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object, + "underlyingSink.start", controller); + ASSERT_TRUE(algo); + ExceptionState exception_state(scope.GetIsolate(), + ExceptionState::kExecutionContext, "", ""); + auto maybe_result = algo->Run(scope.GetScriptState(), exception_state); + EXPECT_TRUE(exception_state.HadException()); + EXPECT_TRUE(maybe_result.IsEmpty()); +} + +TEST(MiscellaneousOperationsTest, CreateStartAlgorithmReturningController) { + V8TestingScope scope; + ScriptValue underlying_value = EvalWithPrintingError(&scope, + R"(({ + start(controller) { + return controller; + } +}))"); + ASSERT_TRUE(underlying_value.IsObject()); + auto underlying_object = underlying_value.V8Value().As<v8::Object>(); + // In a real stream, |controller| is never a promise, but nothing in + // CreateStartAlgorithm() requires this. By making it a promise, we can verify + // that a promise returned from start is passed through as-is. + v8::Local<v8::Value> controller = + v8::Promise::Resolver::New(scope.GetContext()) + .ToLocalChecked() + ->GetPromise(); + auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object, + "underlyingSink.start", controller); + ASSERT_TRUE(algo); + auto maybe_result = algo->Run(scope.GetScriptState(), ASSERT_NO_EXCEPTION); + EXPECT_FALSE(maybe_result.IsEmpty()); + v8::Local<v8::Value> result = maybe_result.ToLocalChecked(); + ASSERT_TRUE(result->IsPromise()); + ASSERT_EQ(result, controller); +} + TEST(MiscellaneousOperationsTest, CallOrNoop1NoMethod) { V8TestingScope scope; auto underlying_object = v8::Object::New(scope.GetIsolate());
diff --git a/third_party/blink/renderer/core/streams/stream_algorithms.h b/third_party/blink/renderer/core/streams/stream_algorithms.h index c4f2dd78..a0217a9 100644 --- a/third_party/blink/renderer/core/streams/stream_algorithms.h +++ b/third_party/blink/renderer/core/streams/stream_algorithms.h
@@ -34,10 +34,23 @@ virtual void Trace(Visitor*) {} }; +// Base class for start algorithms, ie. those that are derived from the start() +// method of the underlying object. These differ from other underlying +// algorithms in that they can throw synchronously. Objects of this +// type must always be reachable by V8's garbage collector. +class StreamStartAlgorithm : public GarbageCollectedFinalized<StreamAlgorithm> { + public: + virtual ~StreamStartAlgorithm() = default; + + virtual v8::MaybeLocal<v8::Promise> Run(ScriptState*, ExceptionState&) = 0; + + virtual void Trace(Visitor*) {} +}; + // Base class for algorithms which take one or more arguments and return a // Promise. This is used as the type for all the algorithms in the standard that -// do not use StrategySizeAlgorithm. Objects of this type must always be -// reachable by V8's garbage collector. +// do not use StrategySizeAlgorithm or StreamStartAlgorithm. Objects of this +// type must always be reachable by V8's garbage collector. class StreamAlgorithm : public GarbageCollectedFinalized<StreamAlgorithm> { public: virtual ~StreamAlgorithm() = default;
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc index cf062fa..f6683ab 100644 --- a/third_party/blink/renderer/core/workers/worker_thread.cc +++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -465,7 +465,7 @@ WorkerThreadDebugger::From(GetIsolate())) debugger->WorkerThreadCreated(this); - if (GlobalScope()->ScriptController()->InitializeContextIfNeeded( + if (GlobalScope()->ScriptController()->InitializeContext( String(), url_for_debugger)) { worker_reporting_proxy_.DidInitializeWorkerContext(); v8::HandleScope handle_scope(GetIsolate());
diff --git a/third_party/blink/renderer/modules/DEPS b/third_party/blink/renderer/modules/DEPS index b0697f0..3122bc0 100644 --- a/third_party/blink/renderer/modules/DEPS +++ b/third_party/blink/renderer/modules/DEPS
@@ -5,6 +5,7 @@ "+mojo/public/cpp/bindings", "+services/network/public/cpp/shared_url_loader_factory.h", "+services/service_manager/public/mojom/interface_provider.mojom-blink.h", + "+services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h", "+third_party/blink/public/common", "+third_party/blink/public/web", "+third_party/blink/renderer/bindings",
diff --git a/third_party/blink/renderer/modules/battery/DEPS b/third_party/blink/renderer/modules/battery/DEPS index 87cb07eb..cdfc1ab 100644 --- a/third_party/blink/renderer/modules/battery/DEPS +++ b/third_party/blink/renderer/modules/battery/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+services/device/public/mojom/battery_monitor.mojom-blink.h", + "+services/device/public/mojom/battery_status.mojom-blink.h", "+services/device/public/mojom/constants.mojom-blink.h", "-third_party/blink/renderer/modules", "+third_party/blink/renderer/modules/event_target_modules.h",
diff --git a/third_party/blink/renderer/modules/battery/battery_dispatcher.cc b/third_party/blink/renderer/modules/battery/battery_dispatcher.cc index aa084b6..0ee3b1a 100644 --- a/third_party/blink/renderer/modules/battery/battery_dispatcher.cc +++ b/third_party/blink/renderer/modules/battery/battery_dispatcher.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/modules/battery/battery_dispatcher.h" +#include "services/device/public/mojom/battery_status.mojom-blink.h" #include "services/device/public/mojom/constants.mojom-blink.h" #include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h"
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc index 6be47fe..bff719a 100644 --- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc +++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
@@ -7,6 +7,7 @@ #include <memory> #include "build/build_config.h" +#include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom-blink.h"
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc index 93955ac..21c5f15 100644 --- a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc +++ b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h" +#include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/dom/document.h"
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc index fd6e0ba..bf74acd 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -101,8 +101,8 @@ pending_generator_registry); String context_name("PaintWorklet #"); context_name.append(String::Number(global_scope_number)); - global_scope->ScriptController()->InitializeContextIfNeeded(context_name, - NullURL()); + // TODO(bashi): Handle a case where the script controller fails to initialize. + global_scope->ScriptController()->InitializeContext(context_name, NullURL()); MainThreadDebugger::Instance()->ContextCreated( global_scope->ScriptController()->GetScriptState(), global_scope->GetFrame(), global_scope->DocumentSecurityOrigin());
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc index 519d1b46..0f71a86 100644 --- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc +++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -180,9 +180,10 @@ devtools_worker_token_ = data.devtools_worker_token; // S13nServiceWorker: |loader_factory| is null since all loads for new scripts - // go through ServiceWorkerNetworkProvider::script_loader_factory() rather - // than the shadow page's loader. This is different to shared workers, which - // use script_loader_factory() for the main script only, and the shadow page + // go through (internal WebServiceWorkerNetworkProvider class in + // service_worker_context_client.cc)::script_loader_factory() rather than the + // shadow page's loader. This is different to shared workers, which use + // script_loader_factory() for the main script only, and the shadow page // loader for importScripts(). // // Non-S13nServiceWorker: |loader_factory| is null since the main script load
diff --git a/third_party/blink/renderer/modules/payments/can_make_payment_test.cc b/third_party/blink/renderer/modules/payments/can_make_payment_test.cc index ac712f7..21860ed 100644 --- a/third_party/blink/renderer/modules/payments/can_make_payment_test.cc +++ b/third_party/blink/renderer/modules/payments/can_make_payment_test.cc
@@ -17,12 +17,111 @@ using payments::mojom::blink::PaymentErrorReason; using payments::mojom::blink::PaymentRequestClient; -// HasEnrolledInstrumentTest is parameterized on this enum to test that -// canMakePayment when PaymentRequestHasEnrolledInstrumentEnabled is false -// behaves identically to hasEnrolledInstrument. +class HasEnrolledInstrumentTest : public testing::Test { + void SetUp() override { + testing::Test::SetUp(); + RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(true); + } +}; + +TEST_F(HasEnrolledInstrumentTest, RejectPromiseOnUserCancel) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); + + static_cast<PaymentRequestClient*>(request)->OnError( + PaymentErrorReason::USER_CANCEL); +} + +TEST_F(HasEnrolledInstrumentTest, RejectPromiseOnUnknownError) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); + + static_cast<PaymentRequestClient*>(request)->OnError( + PaymentErrorReason::UNKNOWN); +} + +TEST_F(HasEnrolledInstrumentTest, RejectDuplicateRequest) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + request->hasEnrolledInstrument(scope.GetScriptState()); + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); +} + +TEST_F(HasEnrolledInstrumentTest, RejectQueryQuotaExceeded) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); + + static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( + HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED); +} + +TEST_F(HasEnrolledInstrumentTest, ReturnHasNoEnrolledInstrument) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + String captor; + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); + + static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( + HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT); + + v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate()); + EXPECT_EQ("false", captor); +} + +TEST_F(HasEnrolledInstrumentTest, ReturnHasEnrolledInstrument) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + String captor; + request->hasEnrolledInstrument(scope.GetScriptState()) + .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); + + static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( + HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT); + + v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate()); + EXPECT_EQ("true", captor); +} + +// CanMakePaymentTest is parameterized on this enum to test both legacy and new +// behaviors. enum class HasEnrolledInstrumentEnabled { kYes, kNo }; -class HasEnrolledInstrumentTest +class CanMakePaymentTest : public testing::Test, public testing::WithParamInterface<HasEnrolledInstrumentEnabled> { void SetUp() override { @@ -32,146 +131,7 @@ } }; -TEST_P(HasEnrolledInstrumentTest, RejectPromiseOnUserCancel) { - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } else { - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } - - static_cast<PaymentRequestClient*>(request)->OnError( - PaymentErrorReason::USER_CANCEL); -} - -TEST_P(HasEnrolledInstrumentTest, RejectPromiseOnUnknownError) { - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } else { - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } - - static_cast<PaymentRequestClient*>(request)->OnError( - PaymentErrorReason::UNKNOWN); -} - -TEST_P(HasEnrolledInstrumentTest, RejectDuplicateRequest) { - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()); - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } else { - request->canMakePayment(scope.GetScriptState()); - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } -} - -TEST_P(HasEnrolledInstrumentTest, RejectQueryQuotaExceeded) { - RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(false); - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } else { - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); - } - - static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( - HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED); -} - -TEST_P(HasEnrolledInstrumentTest, ReturnHasNoEnrolledInstrument) { - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - String captor; - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); - } else { - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); - } - - static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( - HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT); - - v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate()); - EXPECT_EQ("false", captor); -} - -TEST_P(HasEnrolledInstrumentTest, ReturnHasEnrolledInstrument) { - V8TestingScope scope; - PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); - MakePaymentRequestOriginSecure(scope.GetDocument()); - PaymentRequest* request = PaymentRequest::Create( - scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), - BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); - String captor; - if (GetParam() == HasEnrolledInstrumentEnabled::kYes) { - request->hasEnrolledInstrument(scope.GetScriptState()) - .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); - } else { - request->canMakePayment(scope.GetScriptState()) - .Then(funcs.ExpectCall(&captor), funcs.ExpectNoCall()); - } - - static_cast<PaymentRequestClient*>(request)->OnHasEnrolledInstrument( - HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT); - - v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate()); - EXPECT_EQ("true", captor); -} - -INSTANTIATE_TEST_CASE_P(ProgrammaticHasEnrolledInstrumentTest, - HasEnrolledInstrumentTest, - ::testing::Values(HasEnrolledInstrumentEnabled::kYes, - HasEnrolledInstrumentEnabled::kNo)); - -// Test fixture for canMakePayment when -// PaymentRequestHasEnrolledInstrumentEnabled is true. -class CanMakePaymentTest : public testing::Test { - void SetUp() override { - testing::Test::SetUp(); - RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(true); - } -}; - -TEST_F(CanMakePaymentTest, RejectPromiseOnUserCancel) { +TEST_P(CanMakePaymentTest, RejectPromiseOnUserCancel) { V8TestingScope scope; PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); MakePaymentRequestOriginSecure(scope.GetDocument()); @@ -186,7 +146,7 @@ PaymentErrorReason::USER_CANCEL); } -TEST_F(CanMakePaymentTest, RejectPromiseOnUnknownError) { +TEST_P(CanMakePaymentTest, RejectPromiseOnUnknownError) { V8TestingScope scope; PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); MakePaymentRequestOriginSecure(scope.GetDocument()); @@ -201,7 +161,7 @@ PaymentErrorReason::UNKNOWN); } -TEST_F(CanMakePaymentTest, RejectDuplicateRequest) { +TEST_P(CanMakePaymentTest, RejectDuplicateRequest) { V8TestingScope scope; PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); MakePaymentRequestOriginSecure(scope.GetDocument()); @@ -214,7 +174,22 @@ .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); } -TEST_F(CanMakePaymentTest, ReturnCannotMakePayment) { +TEST_P(CanMakePaymentTest, RejectQueryQuotaExceeded) { + V8TestingScope scope; + PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); + MakePaymentRequestOriginSecure(scope.GetDocument()); + PaymentRequest* request = PaymentRequest::Create( + scope.GetExecutionContext(), BuildPaymentMethodDataForTest(), + BuildPaymentDetailsInitForTest(), scope.GetExceptionState()); + + request->canMakePayment(scope.GetScriptState()) + .Then(funcs.ExpectNoCall(), funcs.ExpectCall()); + + static_cast<PaymentRequestClient*>(request)->OnCanMakePayment( + CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED); +} + +TEST_P(CanMakePaymentTest, ReturnCannotMakePayment) { V8TestingScope scope; PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); MakePaymentRequestOriginSecure(scope.GetDocument()); @@ -232,7 +207,7 @@ EXPECT_EQ("false", captor); } -TEST_F(CanMakePaymentTest, ReturnCanMakePayment) { +TEST_P(CanMakePaymentTest, ReturnCanMakePayment) { V8TestingScope scope; PaymentRequestMockFunctionScope funcs(scope.GetScriptState()); MakePaymentRequestOriginSecure(scope.GetDocument()); @@ -250,5 +225,10 @@ EXPECT_EQ("true", captor); } +INSTANTIATE_TEST_CASE_P(ProgrammaticCanMakePaymentTest, + CanMakePaymentTest, + ::testing::Values(HasEnrolledInstrumentEnabled::kYes, + HasEnrolledInstrumentEnabled::kNo)); + } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc index 62ea7cdc8..c25412e7 100644 --- a/third_party/blink/renderer/modules/payments/payment_request.cc +++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -88,6 +88,9 @@ using ::payments::mojom::blink::PaymentValidationErrors; using ::payments::mojom::blink::PaymentValidationErrorsPtr; +const char kCanMakePaymentDebugName[] = "canMakePayment"; +const char kHasEnrolledInstrumentDebugName[] = "hasEnrolledInstrument"; + } // namespace namespace mojo { @@ -767,12 +770,15 @@ } void WarnIgnoringQueryQuotaForCanMakePayment( - ExecutionContext& execution_context) { - execution_context.AddConsoleMessage(ConsoleMessage::Create( - kJSMessageSource, kWarningMessageLevel, - "Quota reached for PaymentRequest.canMakePayment(). This would normally " + ExecutionContext& execution_context, + const char* method_name) { + const String& error = String::Format( + "Quota reached for PaymentRequest.%s(). This would normally " "reject the promise, but allowing continued usage on localhost and " - "file:// scheme origins.")); + "file:// scheme origins.", + method_name); + execution_context.AddConsoleMessage( + ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, error)); } } // namespace @@ -864,12 +870,6 @@ } ScriptPromise PaymentRequest::canMakePayment(ScriptState* script_state) { - if (!RuntimeEnabledFeatures::PaymentRequestHasEnrolledInstrumentEnabled()) { - // Fallback to backward-compatible definition of canMakePayment, which is - // now implemented as hasEnrolledInstrument. - return hasEnrolledInstrument(script_state); - } - if (!payment_provider_.is_bound() || GetPendingAcceptPromiseResolver() || can_make_payment_resolver_ || !script_state->ContextIsValid()) { return ScriptPromise::RejectWithDOMException( @@ -877,7 +877,9 @@ "Cannot query payment request")); } - payment_provider_->CanMakePayment(); + bool legacy_mode = + !RuntimeEnabledFeatures::PaymentRequestHasEnrolledInstrumentEnabled(); + payment_provider_->CanMakePayment(legacy_mode); can_make_payment_resolver_ = ScriptPromiseResolver::Create(script_state); return can_make_payment_resolver_->Promise(); @@ -1444,16 +1446,31 @@ } void PaymentRequest::OnCanMakePayment(CanMakePaymentQueryResult result) { + // TODO(https://crbug.com/891371): Understand how the resolver could be null + // here and prevent it. if (!can_make_payment_resolver_) return; switch (result) { + case CanMakePaymentQueryResult::WARNING_CAN_MAKE_PAYMENT: + WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext(), + kCanMakePaymentDebugName); + FALLTHROUGH; case CanMakePaymentQueryResult::CAN_MAKE_PAYMENT: can_make_payment_resolver_->Resolve(true); break; + case CanMakePaymentQueryResult::WARNING_CANNOT_MAKE_PAYMENT: + WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext(), + kCanMakePaymentDebugName); + FALLTHROUGH; case CanMakePaymentQueryResult::CANNOT_MAKE_PAYMENT: can_make_payment_resolver_->Resolve(false); break; + case CanMakePaymentQueryResult::QUERY_QUOTA_EXCEEDED: + can_make_payment_resolver_->Reject(DOMException::Create( + DOMExceptionCode::kNotAllowedError, + "Not allowed to check whether can make payment")); + break; } can_make_payment_resolver_.Clear(); @@ -1468,13 +1485,15 @@ switch (result) { case HasEnrolledInstrumentQueryResult::WARNING_HAS_ENROLLED_INSTRUMENT: - WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext()); + WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext(), + kHasEnrolledInstrumentDebugName); FALLTHROUGH; case HasEnrolledInstrumentQueryResult::HAS_ENROLLED_INSTRUMENT: has_enrolled_instrument_resolver_->Resolve(true); break; case HasEnrolledInstrumentQueryResult::WARNING_HAS_NO_ENROLLED_INSTRUMENT: - WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext()); + WarnIgnoringQueryQuotaForCanMakePayment(*GetExecutionContext(), + kHasEnrolledInstrumentDebugName); FALLTHROUGH; case HasEnrolledInstrumentQueryResult::HAS_NO_ENROLLED_INSTRUMENT: has_enrolled_instrument_resolver_->Resolve(false); @@ -1482,7 +1501,7 @@ case HasEnrolledInstrumentQueryResult::QUERY_QUOTA_EXCEEDED: has_enrolled_instrument_resolver_->Reject(DOMException::Create( DOMExceptionCode::kNotAllowedError, - "Not allowed to check whether can make payment")); + "Exceeded query quota for hasEnrolledInstrument")); break; }
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h index a6d923ed..c3ce1dfe 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream.h
@@ -77,7 +77,8 @@ // for reading and writing and deleted by the quic::QuicSession. virtual void WriteData(Vector<uint8_t> data, bool fin) = 0; - // Sets the delegate object, which must outlive the P2PQuicStream. + // Sets the delegate object. In the case that the Delegate does not outlive + // the P2PQuicStream object, it must unset itself before destructing. virtual void SetDelegate(Delegate* delegate) = 0; };
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc index 567214b7..b53479f 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h" +#include <utility> #include "net/third_party/quic/core/quic_error_codes.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" @@ -34,13 +35,14 @@ P2PQuicStreamImpl::~P2PQuicStreamImpl() {} void P2PQuicStreamImpl::OnDataAvailable() { - DCHECK(delegate_); if (!sequencer()->HasBytesToRead() && sequencer()->IsClosed()) { // We have consumed all data from the sequencer up to the FIN bit. This can // only occur by receiving an empty STREAM frame with the FIN bit set. quic::QuicStream::OnFinRead(); - delegate_->OnDataReceived({}, /*fin=*/true); consumed_fin_ = true; + if (delegate_) { + delegate_->OnDataReceived({}, /*fin=*/true); + } } uint32_t delegate_read_buffer_available = @@ -87,17 +89,20 @@ quic::QuicStream::OnFinRead(); consumed_fin_ = fin; } - delegate_->OnDataReceived(std::move(data), fin); + if (delegate_) { + delegate_->OnDataReceived(std::move(data), fin); + } } void P2PQuicStreamImpl::OnStreamDataConsumed(size_t bytes_consumed) { - DCHECK(delegate_); // We should never consume more than has been written. DCHECK_GE(write_buffered_amount_, bytes_consumed); QuicStream::OnStreamDataConsumed(bytes_consumed); if (bytes_consumed > 0) { write_buffered_amount_ -= bytes_consumed; - delegate_->OnWriteDataConsumed(SafeCast<uint32_t>(bytes_consumed)); + if (delegate_) { + delegate_->OnWriteDataConsumed(SafeCast<uint32_t>(bytes_consumed)); + } } } @@ -134,12 +139,13 @@ } void P2PQuicStreamImpl::OnStreamReset(const quic::QuicRstStreamFrame& frame) { - DCHECK(delegate_); // Calling this on the QuicStream will ensure that the stream is closed // for reading and writing and we send a RST frame to the remote side if // we have not already. quic::QuicStream::OnStreamReset(frame); - delegate_->OnRemoteReset(); + if (delegate_) { + delegate_->OnRemoteReset(); + } } void P2PQuicStreamImpl::OnClose() {
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h index 733ff5a..2af3a5f 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_impl.h
@@ -67,7 +67,7 @@ private: using quic::QuicStream::Reset; - // Outlives the P2PQuicStreamImpl. + // Must either outlive the P2PQuicStream or unset itself upon destruction. Delegate* delegate_; // The read buffer size of the delegate. The |delegate_read_buffered_amount_|
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc index 0a36ce34..d3c03c7 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc +++ b/third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_stream_unittest.cc
@@ -439,4 +439,64 @@ // Just the last data received ("ta") is held in the delegate's read buffer. EXPECT_EQ(2u, stream_->DelegateReadBufferedAmountForTesting()); } + +// Tests that after a delegate unsets itself, it will no longer receive the +// OnWriteDataConsumed callback. +TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnWriteDataConsumed) { + InitializeStream(); + stream_->SetDelegate(nullptr); + // Mock out the QuicSession to get the QuicStream::OnStreamDataConsumed + // callback to fire. + EXPECT_CALL(session_, + WritevData(stream_, kStreamId, + /*write_length=*/base::size(kSomeData), _, _)) + .WillOnce(Invoke([](quic::QuicStream* stream, quic::QuicStreamId id, + size_t write_length, quic::QuicStreamOffset offset, + quic::StreamSendingState state) { + return quic::QuicConsumedData( + write_length, state != quic::StreamSendingState::NO_FIN); + })); + + EXPECT_CALL(delegate_, OnWriteDataConsumed(_)).Times(0); + + stream_->WriteData(VectorFromArray(kSomeData), /*fin=*/false); +} + +// Tests that after a delegate unsets itself, it will no longer receive the +// OnRemoteReset callback. +TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnRemoteReset) { + InitializeStream(); + stream_->SetDelegate(nullptr); + EXPECT_CALL(delegate_, OnRemoteReset()).Times(0); + + quic::QuicRstStreamFrame rst_frame(quic::kInvalidControlFrameId, kStreamId, + quic::QUIC_STREAM_CANCELLED, 0); + stream_->OnStreamReset(rst_frame); +} + +// Tests that after a delegate unsets itself, it will no longer receive the +// OnDataReceived callback when receiving a stream frame with data and no FIN +// bit. +TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnDataReceivedWithData) { + InitializeStream(); + stream_->SetDelegate(nullptr); + + EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(0); + + quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/false, 0, + StringPieceFromArray(kSomeData)); + stream_->OnStreamFrame(stream_frame); +} + +// Tests that after a delegate unsets itself, it will no longer receive the +// OnDataReceived callback when receiving a stream frame with the FIN bit. +TEST_F(P2PQuicStreamTest, UnsetDelegateDoesNotFireOnDataReceivedWithFin) { + InitializeStream(); + stream_->SetDelegate(nullptr); + + EXPECT_CALL(delegate_, OnDataReceived(_, _)).Times(0); + + quic::QuicStreamFrame stream_frame(stream_->id(), /*fin=*/true, 0, {}); + stream_->OnStreamFrame(stream_frame); +} } // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc index a0937ef..70debe1 100644 --- a/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc +++ b/third_party/blink/renderer/modules/peerconnection/adapters/quic_stream_host.cc
@@ -99,6 +99,7 @@ void QuicStreamHost::Delete() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK(transport_host_); + p2p_stream_->SetDelegate(nullptr); // OnRemoveStream will delete |this|. transport_host_->OnRemoveStream(this); }
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc index 6acfef6..ac4de7c5 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream.cc
@@ -193,9 +193,6 @@ data_vector.size(), 1, 24000000, 50); proxy_->WriteData(std::move(data_vector), finish); if (finish) { - // TODO(shampson): This can cause a crash. If the P2PQuicStream - // has buffered data with a FIN, it will try calling - // host->OnWriteDataConsumed, after the QuicStreamHost has been deleted. wrote_fin_ = true; if (!read_fin_) { DCHECK_EQ(state_, RTCQuicStreamState::kOpen);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_test.cc index 3ab9b0e..8ae23b7 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_test.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_quic_stream_test.cc
@@ -171,6 +171,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); RunUntilIdle(); @@ -205,6 +207,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); quic_stream->reset(); EXPECT_EQ("closed", quic_stream->state()); @@ -328,6 +332,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); RunUntilIdle(); @@ -358,6 +364,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnDataReceived({}, /*fin=*/true); RunUntilIdle(); @@ -384,6 +392,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); RunUntilIdle(); @@ -575,6 +585,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); RunUntilIdle(); @@ -878,6 +890,8 @@ Persistent<RTCQuicStream> stream = CreateQuicStream(scope, p2p_quic_stream.get()); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream->reset(); NotShared<DOMUint8Array> read_buffer(DOMUint8Array::Create(2)); @@ -1136,6 +1150,8 @@ ASSERT_TRUE(stream_delegate); EXPECT_EQ("open", stream->state()); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnRemoteReset(); RunUntilIdle(); @@ -1167,6 +1183,7 @@ EXPECT_TRUE(stream->readInto(read_buffer, ASSERT_NO_EXCEPTION)->finished()); EXPECT_EQ("closing", stream->state()); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); stream->write(CreateWriteParametersWithoutData(/*finish=*/true), ASSERT_NO_EXCEPTION); @@ -1195,6 +1212,8 @@ RunUntilIdle(); ASSERT_TRUE(stream_delegate); + EXPECT_CALL(*p2p_quic_stream.get(), SetDelegate(nullptr)); + stream_delegate->OnDataReceived({}, /*fin=*/true); RunUntilIdle();
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc index cd30cfd..356ed7ca 100644 --- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc +++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -657,7 +657,7 @@ void ServiceWorkerGlobalScopeProxy::DidLoadInstalledScript() { DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); - Client().WorkerScriptLoaded(); + Client().InstalledWorkerScriptLoaded(); } void ServiceWorkerGlobalScopeProxy::DidFailToLoadInstalledClassicScript() {
diff --git a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc index 8d3a6f6..e071c92 100644 --- a/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc +++ b/third_party/blink/renderer/platform/graphics/begin_frame_provider.cc
@@ -9,6 +9,7 @@ #include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "ui/gfx/mojo/presentation_feedback.mojom-blink.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc index 75731e6..73643de7 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -11,6 +11,7 @@ #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/resources/single_release_callback.h" +#include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h" #include "third_party/blink/public/platform/interface_provider.h" #include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" @@ -22,6 +23,7 @@ #include "third_party/blink/renderer/platform/histogram.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" +#include "ui/gfx/mojo/presentation_feedback.mojom-blink.h" namespace blink {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc index 2603317..21cba56 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
@@ -7,6 +7,7 @@ #include <memory> #include "components/viz/common/quads/texture_draw_quad.h" +#include "services/viz/public/interfaces/hit_test/hit_test_region_list.mojom-blink.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom-blink.h" @@ -17,6 +18,7 @@ #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" #include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/skia/include/core/SkSurface.h" +#include "ui/gfx/mojo/presentation_feedback.mojom-blink.h" using testing::_; using testing::Mock;
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h index 685e85d..ce7379f 100644 --- a/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h +++ b/third_party/blink/renderer/platform/graphics/test/mock_compositor_frame_sink.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_TEST_MOCK_COMPOSITOR_FRAME_SINK_H_ #include "components/viz/common/quads/compositor_frame.h" +#include "gpu/ipc/common/mailbox.mojom-blink.h" #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/blink/public/platform/modules/frame_sinks/embedded_frame_sink.mojom-blink.h"
diff --git a/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc b/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc index 46a82b7..44dca7c0 100644 --- a/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc +++ b/third_party/blink/renderer/platform/mojo/geometry_struct_traits_test.cc
@@ -7,6 +7,7 @@ #include "base/message_loop/message_loop.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/mojo/geometry.mojom-blink.h" #include "ui/gfx/geometry/mojo/geometry_traits_test_service.mojom-blink.h" namespace blink {
diff --git a/third_party/blink/web_tests/SmokeTests b/third_party/blink/web_tests/SmokeTests index 5554052..3f3390c2 100644 --- a/third_party/blink/web_tests/SmokeTests +++ b/third_party/blink/web_tests/SmokeTests
@@ -619,7 +619,6 @@ fast/innerHTML/innerHTML-iframe.html fast/innerHTML/innerHTML-nbsp.xhtml fast/innerHTML/innerHTML-script-tag-crash.xhtml -fast/innerHTML/javascript-url.html fast/input/input-device-constructor.html fast/inspector-support/cssURLQuotes.html fast/inspector-support/uncaught-dom1-exception.html
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index f2782fd2..a5c7f574d 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -5759,7 +5759,7 @@ # Sheriff 2018-11-26 crbug.com/908276 [ Mac ] external/wpt/webstorage/storage_setitem.html [ Pass Timeout ] -crbug.com/908347 [ Mac ] media/autoplay/webaudio-audio-context-resume.html [ Failure Pass ] +crbug.com/908347 media/autoplay/webaudio-audio-context-resume.html [ Failure Pass ] # Sheriff 2018-11-29 crbug.com/910139 custom-elements/form-submission-file.html [ Crash Pass ] @@ -5892,11 +5892,17 @@ crbug.com/922951 animations/direction-and-fill/fill-mode-transform.html [ Skip ] crbug.com/922951 animations/direction-and-fill/fill-mode.html [ Skip ] crbug.com/922951 compositing/overflow/overflow-scroll-with-local-background.html [ Skip ] +crbug.com/922951 fast/dom/shadow/move-marquee-crossing-treescope-crash.html [ Skip ] crbug.com/922951 fast/loader/detach-while-printing.html [ Skip ] crbug.com/922951 fast/loader/document-open-iframe-then-detach.html [ Skip ] crbug.com/922951 fast/scroll-snap/snaps-after-keyboard-scrolling.html [ Skip ] crbug.com/922951 fast/scroll-snap/snaps-for-different-key-granularity.html [ Skip ] crbug.com/922951 fast/text/international/inline-plaintext-relayout-with-leading-neutrals.html [ Skip ] +crbug.com/922951 http/tests/devtools/audits2/audits2-successful-run.js [ Skip ] +crbug.com/922951 http/tests/devtools/tracing/console-timeline.js [ Skip ] +crbug.com/922951 http/tests/devtools/tracing/timeline-network-received-data.js [ Skip ] +crbug.com/922951 http/tests/devtools/tracing-session-id.js [ Skip ] +crbug.com/922951 http/tests/images/feature-policy-unoptimized-images-cached-image.html [ Skip ] crbug.com/922951 http/tests/security/offscreencanvas-placeholder-read-blocked-no-crossorigin.html [ Skip ] crbug.com/922951 http/tests/webaudio/autoplay-crossorigin.html [ Skip ] crbug.com/922951 media/controls/overflow-menu-hide-on-click-outside-stoppropagation.html [ Skip ] @@ -5904,6 +5910,9 @@ crbug.com/922951 media/controls/overflow-menu-toggle-class-for-animation.html [ Skip ] crbug.com/922951 svg/animations/dynamic-modify-transform-without-baseval.html [ Skip ] crbug.com/922951 svg/animations/target-condition-crash.html [ Skip ] +crbug.com/922951 svg/as-object/embedded-svg-immediate-offsetWidth-query.html [ Skip ] +crbug.com/922951 svg/as-object/sizing/svg-in-object-placeholder-fixed-percentage-intrinsic-ratio.html [ Skip ] +crbug.com/922951 svg/custom/object-sizing-zero-intrinsic-width-height.html [ Skip ] crbug.com/922951 virtual/gpu/fast/canvas/color-space/canvas-drawImage-offscreenCanvas.html [ Skip ] crbug.com/922951 virtual/gpu/fast/canvas/OffscreenCanvas-placeholder-createImageBitmap.html [ Skip ] crbug.com/922951 virtual/mouseevent_fractional/fast/events/synthetic-events/tap-on-scaled-screen.html [ Skip ] @@ -5919,6 +5928,12 @@ crbug.com/922951 virtual/threaded/animations/direction-and-fill/fill-mode-iteration-count-non-integer.html [ Skip ] crbug.com/922951 virtual/threaded/animations/direction-and-fill/fill-mode-missing-from-to-keyframes.html [ Skip ] crbug.com/922951 virtual/threaded/animations/direction-and-fill/fill-mode.html [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/console-timeline.js [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/frame-model-instrumentation.js [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/timeline-layout/timeline-layout-with-invalidations.js [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/timeline-layout/timeline-layout-reason.js [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-record-reload.js [ Skip ] +crbug.com/922951 virtual/threaded/http/tests/devtools/tracing/timeline-paint/timeline-paint-image.js [ Skip ] crbug.com/922951 virtual/user-activation-v2/fast/events/synthetic-events/tap-on-scaled-screen.html [ Skip ] crbug.com/922951 virtual/video-surface-layer/media/controls/overflow-menu-hide-on-click-outside-stoppropagation.html [ Skip ] crbug.com/922951 virtual/video-surface-layer/media/controls/overflow-menu-hide-on-click-outside.html [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html index 6645b5aa..1bccbf5 100644 --- a/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html +++ b/third_party/blink/web_tests/external/wpt/html/syntax/serializing-html-fragments/serializing.html
@@ -15,6 +15,7 @@ <span><a b='"'></a></span> <span><a b="<"></a></span> <span><a b=">"></a></span> +<span><a href="javascript:"<>""></a></span> <span><svg xlink:href="a"></svg></span> <span><svg xmlns:svg="test"></svg></span> <span>a</span> @@ -49,6 +50,7 @@ ["<a b=\""\"></a>", "<span><a b=\""\"></a></span>"], ["<a b=\"<\"></a>", "<span><a b=\"<\"></a></span>"], ["<a b=\">\"></a>", "<span><a b=\">\"></a></span>"], +["<a href=\"javascript:"<>"\"></a>", "<span><a href=\"javascript:"<>"\"></a></span>"], ["<svg xlink:href=\"a\"></svg>", "<span><svg xlink:href=\"a\"></svg></span>"], ["<svg xmlns:svg=\"test\"></svg>", "<span><svg xmlns:svg=\"test\"></svg></span>"], ["a", "<span>a</span>"],
diff --git a/third_party/blink/web_tests/fast/innerHTML/javascript-url-expected.txt b/third_party/blink/web_tests/fast/innerHTML/javascript-url-expected.txt deleted file mode 100644 index ee8a57e..0000000 --- a/third_party/blink/web_tests/fast/innerHTML/javascript-url-expected.txt +++ /dev/null
@@ -1,13 +0,0 @@ -Test that innerHTML/outerHTML does not mangle javascript: urls. -PASS r.innerHTML is "<a href=\"javascript:test(123)\">link</a>" -PASS r.innerHTML is "<a href='javascript:test(\"text<\")'>link</a>" -PASS r.innerHTML is "<a href=\"javascript:test('text>')\">link</a>" -PASS r.innerHTML is "<a href=\"javascript:test('text&',"test2&")\">link</a>" -PASS r.firstChild.outerHTML is "<a href=\"javascript:window.location='?x&y'\">link</a>" -PASS r.firstChild.outerHTML is "<a href=\"javascript:window.location='?x&amp;y'\">link</a>" -PASS r.firstChild.outerHTML is "<a href=\"javascript:window.location='?x&y'\">link</a>" -PASS successfullyParsed is true - -TEST COMPLETE - -link
diff --git a/third_party/blink/web_tests/fast/innerHTML/javascript-url.html b/third_party/blink/web_tests/fast/innerHTML/javascript-url.html deleted file mode 100644 index ed1e30a..0000000 --- a/third_party/blink/web_tests/fast/innerHTML/javascript-url.html +++ /dev/null
@@ -1,31 +0,0 @@ -<!DOCTYPE html> -<script src="../../resources/js-test.js"></script> -<body> -Test that innerHTML/outerHTML does not mangle javascript: urls. -<div id=console></div> -<div id=jsurltest><a href='javascript:test(&37;3C!--D--&37;3E)'>link</a></div> -<script> -var r = document.getElementById('jsurltest'); - -r.innerHTML = r.innerHTML.replace('&37;3C!--D--&37;3E', 123); -shouldBeEqualToString('r.innerHTML', '<a href="javascript:test(123)">link</a>'); - -r.firstChild.setAttribute('href', 'javascript:test(\"text<\")'); -shouldBeEqualToString('r.innerHTML', '<a href=\'javascript:test("text<")\'>link</a>'); - -r.firstChild.setAttribute('href', 'javascript:test(\'text>\')'); -shouldBeEqualToString("r.innerHTML", '<a href="javascript:test(\'text>\')">link</a>'); - -testString = 'javascript:test(\'text&\',"test2&")'; -r.firstChild.setAttribute('href', testString); -shouldBeEqualToString('r.innerHTML', '<a href="javascript:test(\'text&\',"test2&")">link</a>'); - -r.firstChild.href = 'javascript:window.location=\'?x&y\''; -shouldBeEqualToString("r.firstChild.outerHTML", '<a href="javascript:window.location=\'?x&y\'">link</a>'); - -// Behavior is same as FF -r.firstChild.href = 'javascript:window.location=\'?x&y\''; -shouldBeEqualToString('r.firstChild.outerHTML', '<a href="javascript:window.location=\'?x&amp;y\'">link</a>'); -r.innerHTML = '<a href="javascript:window.location=\'?x&y\'">link</a>'; -shouldBeEqualToString('r.firstChild.outerHTML', '<a href="javascript:window.location=\'?x&y\'">link</a>'); -</script>
diff --git a/third_party/emoji-segmenter/BUILD.gn b/third_party/emoji-segmenter/BUILD.gn new file mode 100644 index 0000000..a4ba9f4f --- /dev/null +++ b/third_party/emoji-segmenter/BUILD.gn
@@ -0,0 +1,11 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +config("emoji_segmenter_config") { + include_dirs = [ "src" ] +} + +source_set("emoji-segmenter") { + public_configs = [ ":emoji_segmenter_config" ] +}
diff --git a/third_party/emoji-segmenter/LICENSE b/third_party/emoji-segmenter/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/emoji-segmenter/LICENSE
@@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
diff --git a/third_party/emoji-segmenter/OWNERS b/third_party/emoji-segmenter/OWNERS new file mode 100644 index 0000000..a0d6c05 --- /dev/null +++ b/third_party/emoji-segmenter/OWNERS
@@ -0,0 +1,3 @@ +behdad@chromium.org +drott@chromium.org +eae@chromium.org
diff --git a/third_party/emoji-segmenter/README.chromium b/third_party/emoji-segmenter/README.chromium new file mode 100644 index 0000000..904a9d85 --- /dev/null +++ b/third_party/emoji-segmenter/README.chromium
@@ -0,0 +1,16 @@ +Name: Emoji Segmenter +Short Name: emoji-segmenter +URL: https://github.com/googlei18n/emoji-segmenter +Version: 0.1.0 +Date: 20190129 +Revision: 242460e9cbee7453880725be5b9bf352d1882a9f +Security Critical: yes +License: Apache 2.0 +License File: LICENSE + +Description: +This is an import of the Emoji Segmenter maintained at the source URL above. The +Emoji Segmenter is a Ragel grammar and generated C code for segmenting runs of +text into text-presentation and emoji-presentation runs. It is currently used +for deciding which preferred presentation, color or text, a run of text should +have.
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium index af78a9aa..bde85a2c 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: ed7c2ae304814efdb0e85cfbfe5205897b0d8d03 +Revision: 25362a7395755d79755a83ebb5eb19ec6ad0af20 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 ce77d21..acc16da3 100644 --- a/third_party/feed/java_sources.gni +++ b/third_party/feed/java_sources.gni
@@ -231,6 +231,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/EventLogger.java", "src/src/main/java/com/google/android/libraries/feed/piet/host/HostBindingProvider.java", + "src/src/main/java/com/google/android/libraries/feed/piet/host/ThrowingCustomElementProvider.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/AspectRatioScalingImageView.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java", "src/src/main/java/com/google/android/libraries/feed/piet/ui/GradientDrawable.java", @@ -263,8 +264,8 @@ feed_conformance_test_lib_sources = [ "src/src/main/java/com/google/android/libraries/feed/api/common/testing/ContentIdGenerators.java", "src/src/main/java/com/google/android/libraries/feed/api/common/testing/InternalProtocolBuilder.java", + "src/src/main/java/com/google/android/libraries/feed/common/concurrent/testing/ClockBackedFakeMainThreadRunner.java", "src/src/main/java/com/google/android/libraries/feed/common/concurrent/testing/FakeMainThreadRunner.java", - "src/src/main/java/com/google/android/libraries/feed/common/testing/FakeClock.java", "src/src/main/java/com/google/android/libraries/feed/common/testing/FakeRequestManager.java", "src/src/main/java/com/google/android/libraries/feed/common/testing/InfrastructureIntegrationScope.java", "src/src/main/java/com/google/android/libraries/feed/common/testing/ModelProviderValidator.java", @@ -273,6 +274,7 @@ "src/src/main/java/com/google/android/libraries/feed/common/testing/RunnableSubject.java", "src/src/main/java/com/google/android/libraries/feed/common/testing/Suppliers.java", "src/src/main/java/com/google/android/libraries/feed/common/testing/WireProtocolResponseBuilder.java", + "src/src/main/java/com/google/android/libraries/feed/common/time/testing/FakeClock.java", "src/src/main/java/com/google/android/libraries/feed/testing/conformance/network/NetworkClientConformanceTest.java", "src/src/main/java/com/google/android/libraries/feed/testing/conformance/scheduler/SchedulerConformanceTest.java", "src/src/main/java/com/google/android/libraries/feed/testing/conformance/storage/ContentStorageConformanceTest.java",
diff --git a/tools/idl_parser/idl_lexer.py b/tools/idl_parser/idl_lexer.py index a4a6f76..e3965017 100755 --- a/tools/idl_parser/idl_lexer.py +++ b/tools/idl_parser/idl_lexer.py
@@ -161,7 +161,7 @@ # A symbol or keyword. def t_KEYWORD_OR_SYMBOL(self, t): - r'_?[A-Za-z][A-Za-z_0-9-]*' + r'[_-]?[A-Za-z][A-Za-z_0-9-]*' # All non-keywords are assumed to be symbols t.type = self.keywords.get(t.value, 'identifier')
diff --git a/tools/idl_parser/test_lexer/values.in b/tools/idl_parser/test_lexer/values.in index 88dd8cd0..ad285895c 100644 --- a/tools/idl_parser/test_lexer/values.in +++ b/tools/idl_parser/test_lexer/values.in
@@ -54,3 +54,5 @@ identifier blah identifier include-hyphen +identifier _leading_underscore +identifier -leading_hyphen
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 99f7c8b..609aed3 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -7320,6 +7320,18 @@ <int value="14" label="Viber"/> </enum> +<enum name="ClientCertSelectionResult"> + <summary> + This metric enumerates how Chrome choses a client certificate when needed as + a result of user action or content settings. + </summary> + <int value="0" label="User selected certificate"/> + <int value="1" label="User cancelled selection"/> + <int value="2" label="User closed tab"/> + <int value="3" label="Selected by content settings"/> + <int value="4" label="User selection not permitted"/> +</enum> + <enum name="ClipboardAction"> <int value="0" label="Write from non-Incognito"/> <int value="1" label="Write from Incognito"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 18ceb69..ca0e5bf 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -102001,6 +102001,22 @@ </summary> </histogram> +<histogram name="Security.ClientAuth.CertificateSelectionSource" + enum="ClientCertSelectionResult" expires_after="2019-07-30"> + <owner>jdeblasio@chromium.org</owner> + <summary> + When TLS client authentication is requested by the server, Chrome must + choose whether to return a certificate or to abort the handshake. This + histogram records how this determination was made, either as a result of + user action, or as chosen automatically due to content settings. It only + records when a fresh certificate selection would be attempted (and thus + ignores cache hits). This histogram is recorded in SSLClientAuthObserver for + user-selected outcomes and in ChromeContentBrowserClient for policy + outcomes. This histogram does not cover mobile, as certificate handling is + not handled by Chrome on Android nor iOS. + </summary> +</histogram> + <histogram name="Security.HTTPBad.NavigationStartedAfterUserWarnedAboutSensitiveInput" units="ms">
diff --git a/ui/aura/test/DEPS b/ui/aura/test/DEPS index 2de1840..a1c39352 100644 --- a/ui/aura/test/DEPS +++ b/ui/aura/test/DEPS
@@ -2,6 +2,7 @@ "+cc/test", "+components/viz/test", "+mojo/core/embedder/embedder.h", + "+services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h", "+services/ws/public/cpp/input_devices", "+ui/gl", "+ui/wm/core/wm_state.h",
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc index 2357a53..98bf5a3 100644 --- a/ui/aura/test/mus/test_window_tree.cc +++ b/ui/aura/test/mus/test_window_tree.cc
@@ -7,6 +7,8 @@ #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" +#include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h" +#include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/test/mus/test_window_tree_delegate.h"
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h index e491a352..3d15e2be 100644 --- a/ui/aura/test/mus/test_window_tree.h +++ b/ui/aura/test/mus/test_window_tree.h
@@ -14,6 +14,7 @@ #include "services/ws/public/mojom/window_tree.mojom.h" #include "ui/aura/mus/mus_types.h" #include "ui/base/hit_test.h" +#include "ui/platform_window/mojo/text_input_state.mojom.h" namespace aura {
diff --git a/ui/file_manager/file_manager/background/js/test_util.js b/ui/file_manager/file_manager/background/js/test_util.js index 974d5f9..92ba7d1 100644 --- a/ui/file_manager/file_manager/background/js/test_util.js +++ b/ui/file_manager/file_manager/background/js/test_util.js
@@ -17,26 +17,6 @@ }; /** - * Returns an array with the files currently selected in the file manager. - * TODO(hirono): Integrate the method into getFileList method. - * - * @param {Window} contentWindow Window to be tested. - * @return {Array<string>} Array of selected files. - */ -test.util.sync.getSelectedFiles = function(contentWindow) { - var table = contentWindow.document.querySelector('#detail-table'); - var rows = table.querySelectorAll('li'); - var selected = []; - for (var i = 0; i < rows.length; ++i) { - if (rows[i].hasAttribute('selected')) { - selected.push( - rows[i].querySelector('.filename-label').textContent); - } - } - return selected; -}; - -/** * Returns the name of the item currently selected in the directory tree. * Returns null if no entry is selected. * @@ -56,15 +36,16 @@ return null; }; - /** - * Returns an array with the files on the file manager's file list. + * Returns details about each file shown in the file list: name, size, type and + * modification time. * - * TODO(sashab): Since we recycle DOM elements, this only returns the first ~11 - * visible elements. crbug.com/850834. + * Since FilesApp normally has a fixed display size in test, and also since the + * #detail-table recycles its file row elements, this call only returns details + * about the visible file rows (11 rows normally, see crbug.com/850834). * * @param {Window} contentWindow Window to be tested. - * @return {Array<Array<string>>} Array of rows. + * @return {Array<Array<string>>} Details for each visible file row. */ test.util.sync.getFileList = function(contentWindow) { var table = contentWindow.document.querySelector('#detail-table'); @@ -83,6 +64,25 @@ }; /** + * Returns the name of the files currently selected in the file list. Note the + * routine has the same 'visible files' limitation as getFileList() above. + * + * @param {Window} contentWindow Window to be tested. + * @return {Array<string>} Selected file names. + */ +test.util.sync.getSelectedFiles = function(contentWindow) { + var table = contentWindow.document.querySelector('#detail-table'); + var rows = table.querySelectorAll('li'); + var selected = []; + for (var i = 0; i < rows.length; ++i) { + if (rows[i].hasAttribute('selected')) { + selected.push(rows[i].querySelector('.filename-label').textContent); + } + } + return selected; +}; + +/** * Fakes pressing the down arrow until the given |filename| is selected. * * @param {Window} contentWindow Window to be tested.
diff --git a/ui/file_manager/file_manager/foreground/css/file_types.css b/ui/file_manager/file_manager/foreground/css/file_types.css index 1f9609f..9a8da83 100644 --- a/ui/file_manager/file_manager/foreground/css/file_types.css +++ b/ui/file_manager/file_manager/foreground/css/file_types.css
@@ -363,7 +363,8 @@ .external-media-root[file-type-icon='folder'], [volume-type-icon='external_media'], -[volume-type-icon='removable'] { +[volume-type-icon='removable'], +[root-type-icon='removable'] { background-image: -webkit-image-set( url(../images/volumes/usb.png) 1x, url(../images/volumes/2x/usb.png) 2x); @@ -372,7 +373,8 @@ tree .tree-item[selected] > .tree-row > .external-media-root[file-type-icon='folder'], .tree-row[selected] [volume-type-icon='external_media'], -.tree-row[selected] [volume-type-icon='removable'] { +.tree-row[selected] [volume-type-icon='removable'], +.tree-row[selected] [root-type-icon='removable'] { background-image: -webkit-image-set( url(../images/volumes/usb_active.png) 1x, url(../images/volumes/2x/usb_active.png) 2x); @@ -453,13 +455,17 @@ url(../images/volumes/2x/phone_active.png) 2x); } -[volume-type-icon='removable'][volume-subtype='unknown'] { +[volume-type-icon='removable'][volume-subtype='partition'], +[volume-type-icon='removable'][volume-subtype='unknown'], +[file-type-icon='removable'] { background-image: -webkit-image-set( url(../images/volumes/hard_drive.png) 1x, url(../images/volumes/2x/hard_drive.png) 2x); } -.tree-row[selected] [volume-type-icon='removable'][volume-subtype='unknown'] { +tree .tree-item[selected] > .tree-row > +[volume-type-icon='removable'][volume-subtype='partition'], +.tree-row[selected] [volume-type-icon='removable'][volume-subtype='unknown'] { background-image: -webkit-image-set( url(../images/volumes/hard_drive_active.png) 1x, url(../images/volumes/2x/hard_drive_active.png) 2x);
diff --git a/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.cc b/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.cc index b2600ad..d1865c62 100644 --- a/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.cc +++ b/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.cc
@@ -17,6 +17,7 @@ xkb_mod_indexes_.alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); xkb_mod_indexes_.shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); xkb_mod_indexes_.caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + xkb_mod_indexes_.altgr = xkb_keymap_mod_get_index(keymap, "Mod5"); } void WaylandXkbKeyboardLayoutEngine::UpdateModifiers(uint32_t depressed_mods, @@ -48,6 +49,10 @@ event_modifiers_->SetModifierLock(MODIFIER_CAPS_LOCK, true); else event_modifiers_->SetModifierLock(MODIFIER_CAPS_LOCK, false); + + if (xkb_state_mod_index_is_active(xkb_state_.get(), xkb_mod_indexes_.altgr, + component)) + event_modifiers_->UpdateModifier(MODIFIER_ALTGR, true); } void WaylandXkbKeyboardLayoutEngine::SetEventModifiers(
diff --git a/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.h b/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.h index 18c8f8c..70bf303 100644 --- a/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.h +++ b/ui/ozone/platform/wayland/wayland_xkb_keyboard_layout_engine.h
@@ -36,6 +36,7 @@ xkb_mod_index_t alt = 0; xkb_mod_index_t shift = 0; xkb_mod_index_t caps = 0; + xkb_mod_index_t altgr = 0; } xkb_mod_indexes_; EventModifiers* event_modifiers_ = nullptr; // Owned by WaylandKeyboard.