diff --git a/AUTHORS b/AUTHORS
index bf2bffe9..7ad2718 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,6 +36,7 @@
 Alexandre Abreu <wiss1976@gmail.com>
 Alexandru Chiculita <achicu@adobe.com>
 Alexey Korepanov <alexkorep@gmail.com>
+Alexey Kuts <kruntuid@gmail.com>
 Alexis Brenon <brenon.alexis@gmail.com>
 Alexis Menard <alexis.menard@intel.com>
 Alfredo Hernandez <ahernandez.miralles@gmail.com>
diff --git a/DEPS b/DEPS
index a6b6d9b3..182078d 100644
--- a/DEPS
+++ b/DEPS
@@ -43,7 +43,7 @@
   # 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': 'ec21f831870e79aef0417eb1718eefcef9456bff',
+  'v8_revision': 'a964163e6ac4f1124b22ea7e46b8afe26dec02e0',
   # 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.
@@ -95,7 +95,7 @@
   # 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': '03fbd54d8fdb0defa1a677b71d67a9f46544fa86',
+  'catapult_revision': 'f332dd6dd55d4754ecf770f5edc7cc606b13a161',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index 4347ccd..ab52954 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -41,13 +41,7 @@
 android_library("glue") {
   # Change deps? please modify glue_library_deps variable.
   deps = glue_library_deps
-  srcjar_deps = [
-    ":glue_resource_rewriter",
-    "//base:base_build_config_gen",
-  ]
-
-  # New versions of BuildConfig.java will be created when creating an apk.
-  jar_excluded_patterns = [ "*/BuildConfig.class" ]
+  srcjar_deps = [ ":glue_resource_rewriter" ]
 
   # Always build upstream or downstream target with public or internal
   # framework jar respectively.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b704fa9..0cf620f37 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2359,6 +2359,7 @@
     deps = [
       ":base_java",
       "//testing/android/reporter:reporter_java",
+      "//third_party/hamcrest:hamcrest_core_java",
     ]
     java_files = [
       "test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java",
@@ -2377,6 +2378,7 @@
       "test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java",
       "test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java",
       "test/android/javatests/src/org/chromium/base/test/util/Manual.java",
+      "test/android/javatests/src/org/chromium/base/test/util/Matchers.java",
       "test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java",
       "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java",
       "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheck.java",
diff --git a/base/allocator/winheap_stubs_win.cc b/base/allocator/winheap_stubs_win.cc
index 7298bb94..593e386e 100644
--- a/base/allocator/winheap_stubs_win.cc
+++ b/base/allocator/winheap_stubs_win.cc
@@ -35,8 +35,11 @@
   return nullptr;
 }
 
-void WinHeapFree(void* size) {
-  HeapFree(get_heap_handle(), 0, size);
+void WinHeapFree(void* ptr) {
+  if (!ptr)
+    return;
+
+  HeapFree(get_heap_handle(), 0, ptr);
 }
 
 void* WinHeapRealloc(void* ptr, size_t size) {
diff --git a/base/mac/scoped_block.h b/base/mac/scoped_block.h
index 8199677..10ab4b4e 100644
--- a/base/mac/scoped_block.h
+++ b/base/mac/scoped_block.h
@@ -36,9 +36,33 @@
 
 // ScopedBlock<> is patterned after ScopedCFTypeRef<>, but uses Block_copy() and
 // Block_release() instead of CFRetain() and CFRelease().
-
 template <typename B>
-using ScopedBlock = ScopedTypeRef<B, internal::ScopedBlockTraits<B>>;
+class ScopedBlock : public ScopedTypeRef<B, internal::ScopedBlockTraits<B>> {
+ public:
+  using Traits = internal::ScopedBlockTraits<B>;
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+  explicit ScopedBlock(
+      B block = Traits::InvalidValue(),
+      base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+      : ScopedTypeRef<B, Traits>(block, policy) {}
+#else
+  explicit ScopedBlock(B block = Traits::InvalidValue())
+      : ScopedTypeRef<B, Traits>(block, base::scoped_policy::RETAIN) {}
+#endif
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+  void reset(B block = Traits::InvalidValue(),
+             base::scoped_policy::OwnershipPolicy policy =
+                 base::scoped_policy::ASSUME) {
+    ScopedTypeRef<B, Traits>::reset(block, policy);
+  }
+#else
+  void reset(B block = Traits::InvalidValue()) {
+    ScopedTypeRef<B, Traits>::reset(block, base::scoped_policy::RETAIN);
+  }
+#endif
+};
 
 }  // namespace mac
 }  // namespace base
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/Matchers.java b/base/test/android/javatests/src/org/chromium/base/test/util/Matchers.java
new file mode 100644
index 0000000..fc9d6890
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/Matchers.java
@@ -0,0 +1,44 @@
+// 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.base.test.util;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Helper class containing Hamcrest matchers.
+ */
+public class Matchers extends CoreMatchers {
+    private static class GreaterThanOrEqualTo<T extends Comparable<T>>
+            extends TypeSafeMatcher<T> {
+
+        private final T mComparisonValue;
+
+        public GreaterThanOrEqualTo(T comparisonValue) {
+            mComparisonValue = comparisonValue;
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("greater than or equal to ").appendValue(mComparisonValue);
+        }
+
+        @Override
+        protected boolean matchesSafely(T item) {
+            return item.compareTo(mComparisonValue) >= 0;
+        }
+    }
+
+    /**
+     * @param <T> A Comparable type.
+     * @param comparisonValue The value to be compared against.
+     * @return A matcher that expects the value to be greater than the |comparisonValue|.
+     */
+    public static <T extends Comparable<T>> Matcher<T> greaterThanOrEqualTo(T comparisonValue) {
+        return new GreaterThanOrEqualTo<>(comparisonValue);
+    }
+}
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 67e84c0..ad42da6 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1827,11 +1827,23 @@
     }
 
     generate_interface_jar(_ijar_target_name) {
-      input_jar = _jar_path
+      if (!defined(invoker.proguard_preprocess) ||
+          !invoker.proguard_preprocess) {
+        # Always used the unfiltered .jar to create the interface jar so that
+        # other targets will resolve filtered classes when depending on
+        # BuildConfig, NativeLibraries, etc.
+        input_jar = invoker.jar_path
+        deps = _deps + _jar_deps
+      } else {
+        # However, still apply pre-proguarding, since ignoring that can break
+        # compiles.
+        input_jar = _jar_path
+        deps = [
+          ":$_process_jar_target_name",
+        ]
+      }
+
       output_jar = _ijar_path
-      deps = [
-        ":$_process_jar_target_name",
-      ]
     }
 
     if (_supports_android) {
@@ -2128,17 +2140,24 @@
     }
 
     generate_interface_jar(_ijar_target_name) {
-      input_jar = _final_jar_path
-      output_jar = _final_ijar_path
-      if (_emma_instrument) {
+      if (!defined(invoker.proguard_preprocess) ||
+          !invoker.proguard_preprocess) {
+        # Always used the unfiltered .jar to create the interface jar so that
+        # other targets will resolve filtered classes when depending on
+        # BuildConfig, NativeLibraries, etc.
+        input_jar = _javac_jar_path
         deps = [
-          ":$_emma_instr_target_name",
+          ":$_javac_target_name",
         ]
       } else {
+        # However, still apply pre-proguarding, since ignoring that can break
+        # compiles.
+        input_jar = _process_prebuilt_jar_path
         deps = [
           ":$_process_prebuilt_target_name",
         ]
       }
+      output_jar = _final_ijar_path
     }
 
     group(_final_target_name) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index fad3c78f..8a22ed7 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -246,16 +246,12 @@
     ":chrome_android_java_google_api_keys_srcjar",
     ":chrome_version_srcjar",
     ":resource_id_javagen",
-    "//base:base_build_config_gen",
     "//chrome:content_setting_javagen",
     "//chrome:content_settings_type_javagen",
     "//chrome:data_use_ui_message_enum_javagen",
     "//chrome:signin_metrics_enum_javagen",
   ]
 
-  # New versions of BuildConfig.java will be created when creating an apk.
-  jar_excluded_patterns = [ "*/BuildConfig.class" ]
-
   # Manifest used for linting (determining unused resources).
   android_manifest = chrome_public_android_manifest
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 9404990..6931d99 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1779,6 +1779,13 @@
         mContextMenuCloseObservers.addObserver(callback);
     }
 
+    @Override
+    public void onContextMenuClosed(Menu menu) {
+        for (Callback<Menu> callback : mContextMenuCloseObservers) {
+            callback.onResult(menu);
+        }
+    }
+
     /**
      * Removes a {@link Callback} from the list of callbacks that will be triggered when a
      * ContextMenu is closed.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
index 657b0e06..3043f53089 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderBase.java
@@ -7,6 +7,7 @@
 import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.RemoteInput;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -32,21 +33,46 @@
  */
 public abstract class NotificationBuilderBase {
     protected static class Action {
+        enum Type {
+            /**
+             * Regular action that triggers the provided intent when tapped.
+             */
+            BUTTON,
+
+            /**
+             * Action that triggers a remote input when tapped, for Android Wear input and inline
+             * replies from Android N.
+             */
+            TEXT
+        }
+
         public int iconId;
         public Bitmap iconBitmap;
         public CharSequence title;
         public PendingIntent intent;
+        public Type type;
 
-        Action(int iconId, CharSequence title, PendingIntent intent) {
+        /**
+         * If the action.type is TEXT, this corresponds to the placeholder text for the input.
+         */
+        public String placeholder;
+
+        Action(int iconId, CharSequence title, PendingIntent intent, Type type,
+                String placeholder) {
             this.iconId = iconId;
             this.title = title;
             this.intent = intent;
+            this.type = type;
+            this.placeholder = placeholder;
         }
 
-        Action(Bitmap iconBitmap, CharSequence title, PendingIntent intent) {
+        Action(Bitmap iconBitmap, CharSequence title, PendingIntent intent, Type type,
+                String placeholder) {
             this.iconBitmap = iconBitmap;
             this.title = title;
             this.intent = intent;
+            this.type = type;
+            this.placeholder = placeholder;
         }
     }
 
@@ -202,11 +228,28 @@
     }
 
     /**
-     * Adds an action to the notification. Actions are typically displayed as a button adjacent to
-     * the notification content.
+     * Adds an action to the notification, displayed as a button adjacent to the notification
+     * content.
      */
-    public NotificationBuilderBase addAction(@Nullable Bitmap iconBitmap,
+    public NotificationBuilderBase addButtonAction(@Nullable Bitmap iconBitmap,
             @Nullable CharSequence title, @Nullable PendingIntent intent) {
+        addAuthorProvidedAction(iconBitmap, title, intent, Action.Type.BUTTON, null);
+        return this;
+    }
+
+    /**
+     * Adds an action to the notification, displayed as a button adjacent to the notification
+     * content, which when tapped will trigger a remote input. This enables Android Wear input and,
+     * from Android N, displays a text box within the notification for inline replies.
+     */
+    public NotificationBuilderBase addTextAction(@Nullable Bitmap iconBitmap,
+            @Nullable CharSequence title, @Nullable PendingIntent intent, String placeholder) {
+        addAuthorProvidedAction(iconBitmap, title, intent, Action.Type.TEXT, placeholder);
+        return this;
+    }
+
+    private void addAuthorProvidedAction(@Nullable Bitmap iconBitmap, @Nullable CharSequence title,
+            @Nullable PendingIntent intent, Action.Type actionType, @Nullable String placeholder) {
         if (mActions.size() == MAX_AUTHOR_PROVIDED_ACTION_BUTTONS) {
             throw new IllegalStateException(
                     "Cannot add more than " + MAX_AUTHOR_PROVIDED_ACTION_BUTTONS + " actions.");
@@ -214,8 +257,7 @@
         if (iconBitmap != null) {
             applyWhiteOverlayToBitmap(iconBitmap);
         }
-        mActions.add(new Action(iconBitmap, limitLength(title), intent));
-        return this;
+        mActions.add(new Action(iconBitmap, limitLength(title), intent, actionType, placeholder));
     }
 
     /**
@@ -223,7 +265,7 @@
      */
     public NotificationBuilderBase addSettingsAction(
             int iconId, @Nullable CharSequence title, @Nullable PendingIntent intent) {
-        mSettingsAction = new Action(iconId, limitLength(title), intent);
+        mSettingsAction = new Action(iconId, limitLength(title), intent, Action.Type.BUTTON, null);
         return this;
     }
 
@@ -369,17 +411,38 @@
      * level is high enough, otherwise a resource id is used.
      */
     @SuppressWarnings("deprecation") // For addAction(int, CharSequence, PendingIntent)
-    @TargetApi(Build.VERSION_CODES.M) // For the Icon class.
     protected static void addActionToBuilder(Notification.Builder builder, Action action) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && action.iconBitmap != null) {
-            Icon icon = Icon.createWithBitmap(action.iconBitmap);
-            builder.addAction(
-                    new Notification.Action.Builder(icon, action.title, action.intent).build());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+            // Notification.Action.Builder and RemoteInput were added in KITKAT_WATCH.
+            Notification.Action.Builder actionBuilder = getActionBuilder(action);
+            if (action.type == Action.Type.TEXT) {
+                assert action.placeholder != null;
+                actionBuilder.addRemoteInput(
+                        new RemoteInput.Builder(NotificationConstants.KEY_TEXT_REPLY)
+                                .setLabel(action.placeholder)
+                                .build());
+            }
+            builder.addAction(actionBuilder.build());
         } else {
             builder.addAction(action.iconId, action.title, action.intent);
         }
     }
 
+    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) // For Notification.Action.Builder
+    @SuppressWarnings("deprecation") // For Builder(int, CharSequence, PendingIntent)
+    private static Notification.Action.Builder getActionBuilder(Action action) {
+        Notification.Action.Builder actionBuilder;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && action.iconBitmap != null) {
+            // Icon was added in Android M.
+            Icon icon = Icon.createWithBitmap(action.iconBitmap);
+            actionBuilder = new Notification.Action.Builder(icon, action.title, action.intent);
+        } else {
+            actionBuilder =
+                    new Notification.Action.Builder(action.iconId, action.title, action.intent);
+        }
+        return actionBuilder;
+    }
+
     /**
      * Paints {@code bitmap} white. This processing should be performed if the Android system
      * expects a bitmap to be white, and the bitmap is not already known to be white. The bitmap
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
index d44b897..521d7de94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -54,4 +54,9 @@
      * developer specified tag.
      */
     public static final String NOTIFICATION_TAG_SEPARATOR = ";";
+
+    /**
+     * Key for retrieving the results of user input from notification text action intents.
+     */
+    static final String KEY_TEXT_REPLY = "key_text_reply";
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
index afc5c96..0381041 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
@@ -441,6 +441,7 @@
     /**
      * Displays a notification with the given details.
      *
+     * TODO(crbug.com/650302): Combine the 'action*' parameters into a single array of objects.
      * @param notificationId The id of the notification.
      * @param origin Full text of the origin, including the protocol, owning this notification.
      * @param profileId Id of the profile that showed the notification.
@@ -466,13 +467,16 @@
      * @param silent Whether the default sound, vibration and lights should be suppressed.
      * @param actionTitles Titles of actions to display alongside the notification.
      * @param actionIcons Icons of actions to display alongside the notification.
+     * @param actionTypes Types of actions to display alongside the notification.
+     * @param actionPlaceholders Placeholders of actions to display alongside the notification.
      * @see https://developer.android.com/reference/android/app/Notification.html
      */
     @CalledByNative
     private void displayNotification(String notificationId, String origin, String profileId,
             boolean incognito, String tag, String webApkPackage, String title, String body,
             Bitmap image, Bitmap icon, Bitmap badge, int[] vibrationPattern, long timestamp,
-            boolean renotify, boolean silent, String[] actionTitles, Bitmap[] actionIcons) {
+            boolean renotify, boolean silent, String[] actionTitles, Bitmap[] actionIcons,
+            String[] actionTypes, String[] actionPlaceholders) {
         if (actionTitles.length != actionIcons.length) {
             throw new IllegalArgumentException("The number of action titles and icons must match.");
         }
@@ -519,10 +523,17 @@
                                 origin, false /* showScheme */));
 
         for (int actionIndex = 0; actionIndex < actionTitles.length; actionIndex++) {
-            notificationBuilder.addAction(actionIcons[actionIndex], actionTitles[actionIndex],
-                    makePendingIntent(NotificationConstants.ACTION_CLICK_NOTIFICATION,
-                                                  notificationId, origin, profileId, incognito, tag,
-                                                  webApkPackage, actionIndex));
+            PendingIntent intent = makePendingIntent(
+                    NotificationConstants.ACTION_CLICK_NOTIFICATION, notificationId, origin,
+                    profileId, incognito, tag, webApkPackage, actionIndex);
+            // TODO(crbug.com/650302): Encode actionTypes with an enum, not a magic string!
+            if (actionTypes[actionIndex].equals("text")) {
+                notificationBuilder.addTextAction(actionIcons[actionIndex],
+                        actionTitles[actionIndex], intent, actionPlaceholders[actionIndex]);
+            } else {
+                notificationBuilder.addButtonAction(
+                        actionIcons[actionIndex], actionTitles[actionIndex], intent);
+            }
         }
 
         // If action buttons are displayed, there isn't room for the full Site Settings button
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/TitleUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/TitleUtil.java
index df8190b..6285823b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/TitleUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/TitleUtil.java
@@ -5,10 +5,9 @@
 package org.chromium.chrome.browser.ntp;
 
 import android.net.Uri;
+import android.support.annotation.Nullable;
 import android.text.TextUtils;
 
-import javax.annotation.Nullable;
-
 /**
  * Provides functions for working with link titles.
  */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 886dd20d..3701ed4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -442,7 +442,7 @@
     /**
      * Returns an unmodifiable list containing all items in the adapter.
      */
-    public List<NewTabPageItem> getItems() {
+    private List<NewTabPageItem> getItems() {
         List<NewTabPageItem> items = new ArrayList<>();
         for (ItemGroup group : mGroups) {
             items.addAll(group.getItems());
@@ -466,7 +466,8 @@
         return Collections.unmodifiableList(mGroups);
     }
 
-    private int getGroupPositionOffset(ItemGroup group) {
+    @VisibleForTesting
+    int getGroupPositionOffset(ItemGroup group) {
         int positionOffset = 0;
         for (ItemGroup candidateGroup : mGroups) {
             if (candidateGroup == group) return positionOffset;
@@ -474,4 +475,9 @@
         }
         return RecyclerView.NO_POSITION;
     }
+
+    @VisibleForTesting
+    SnippetArticle getSuggestionAt(int position) {
+        return (SnippetArticle) getItems().get(position);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
index 99b19a5cd..351e0d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java
@@ -52,7 +52,7 @@
     /** View used to calculate the position of the cards' snap point. */
     private View mAboveTheFoldView;
 
-    /** Whether the RecyclerView should react to touch events. */
+    /** Whether the RecyclerView and its children should react to touch events. */
     private boolean mTouchEnabled = true;
 
     /** Whether the above-the-fold left space for a peeking card to be displayed. */
@@ -89,6 +89,7 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         mGestureDetector.onTouchEvent(ev);
+        if (!mTouchEnabled) return true;
         return super.onInterceptTouchEvent(ev);
     }
 
@@ -423,7 +424,7 @@
      * Animates the card being swiped to the right as if the user had dismissed it. Any changes to
      * the animation here should be reflected also in
      * {@link #updateViewStateForDismiss(float, ViewHolder)} and reset in
-     * {@link CardViewHolder#onBindViewHolder(NewTabPageItem)}.
+     * {@link CardViewHolder#onBindViewHolder()}.
      * @param suggestion The item to be dismissed.
      */
     public void dismissItemWithAnimation(SnippetArticle suggestion) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
index 1c9d80d..1aa94896 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.notifications;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -23,6 +24,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.R;
 import org.chromium.content.browser.test.NativeLibraryTestBase;
 
@@ -59,23 +61,24 @@
                 new int[] {Color.WHITE}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888);
         actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
 
-        Notification notification =
-                new CustomNotificationBuilder(context)
-                        .setSmallIcon(R.drawable.ic_chrome)
-                        .setLargeIcon(largeIcon)
-                        .setTitle("title")
-                        .setBody("body")
-                        .setOrigin("origin")
-                        .setTicker("ticker")
-                        .setDefaults(Notification.DEFAULT_ALL)
-                        .setVibrate(new long[] {100L})
-                        .setContentIntent(contentIntent)
-                        .setDeleteIntent(deleteIntent)
-                        .addAction(actionIcon, "button", createIntent(context, "ActionButtonOne"))
-                        .addAction(actionIcon, "button", createIntent(context, "ActionButtonTwo"))
-                        .addSettingsAction(
-                                0 /* iconId */, "settings", createIntent(context, "SettingsButton"))
-                        .build();
+        Notification notification = new CustomNotificationBuilder(context)
+                                            .setSmallIcon(R.drawable.ic_chrome)
+                                            .setLargeIcon(largeIcon)
+                                            .setTitle("title")
+                                            .setBody("body")
+                                            .setOrigin("origin")
+                                            .setTicker("ticker")
+                                            .setDefaults(Notification.DEFAULT_ALL)
+                                            .setVibrate(new long[] {100L})
+                                            .setContentIntent(contentIntent)
+                                            .setDeleteIntent(deleteIntent)
+                                            .addButtonAction(actionIcon, "button",
+                                                    createIntent(context, "ActionButtonOne"))
+                                            .addButtonAction(actionIcon, "button",
+                                                    createIntent(context, "ActionButtonTwo"))
+                                            .addSettingsAction(0 /* iconId */, "settings",
+                                                    createIntent(context, "SettingsButton"))
+                                            .build();
 
         assertSmallNotificationIconAsExpected(context, notification, smallIcon);
         assertLargeNotificationIconAsExpected(context, notification, largeIcon);
@@ -137,12 +140,12 @@
     public void testMaxActionButtons() {
         Context context = getInstrumentation().getTargetContext();
         NotificationBuilderBase builder = new CustomNotificationBuilder(context)
-                                                  .addAction(null /* iconBitmap */, "button",
+                                                  .addButtonAction(null /* iconBitmap */, "button",
                                                           createIntent(context, "ActionButtonOne"))
-                                                  .addAction(null /* iconBitmap */, "button",
+                                                  .addButtonAction(null /* iconBitmap */, "button",
                                                           createIntent(context, "ActionButtonTwo"));
         try {
-            builder.addAction(
+            builder.addButtonAction(
                     null /* iconBitmap */, "button", createIntent(context, "ActionButtonThree"));
             fail("This statement should not be reached as the previous statement should throw.");
         } catch (IllegalStateException e) {
@@ -173,12 +176,12 @@
                 new int[] {Color.RED}, 1 /* width */, 1 /* height */, Bitmap.Config.ARGB_8888);
         actionIcon = actionIcon.copy(Bitmap.Config.ARGB_8888, true /* isMutable */);
 
-        Notification notification =
-                new CustomNotificationBuilder(context)
-                        .setLargeIcon(largeIcon)
-                        .setSmallIcon(smallIcon)
-                        .addAction(actionIcon, "button", createIntent(context, "ActionButton"))
-                        .build();
+        Notification notification = new CustomNotificationBuilder(context)
+                                            .setLargeIcon(largeIcon)
+                                            .setSmallIcon(smallIcon)
+                                            .addButtonAction(actionIcon, "button",
+                                                    createIntent(context, "ActionButton"))
+                                            .build();
 
         // The large icon should be unchanged.
         assertLargeNotificationIconAsExpected(context, notification, largeIcon);
@@ -207,7 +210,7 @@
                         .setBody(createString('b', maxLength + 1))
                         .setOrigin(createString('c', maxLength + 1))
                         .setTicker(createString('d', maxLength + 1))
-                        .addAction(null /* iconBitmap */, createString('e', maxLength + 1),
+                        .addButtonAction(null /* iconBitmap */, createString('e', maxLength + 1),
                                 createIntent(context, "ActionButtonOne"))
                         .build();
         View compactView = notification.contentView.apply(context, new LinearLayout(context));
@@ -279,6 +282,30 @@
         assertLargeNotificationIconAsExpected(context, notification, expectedIcon);
     }
 
+    /**
+     * Tests that adding a text action results in a notification action with a RemoteInput attached.
+     *
+     * Note that the action buttons in custom layouts will not trigger an inline reply, but we can
+     * still check the notification's action properties since these are used on Android Wear.
+     */
+    @MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT_WATCH)
+    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) // RemoteInputs were only added in KITKAT_WATCH.
+    @SmallTest
+    @Feature({"Browser", "Notifications"})
+    public void testAddTextActionSetsRemoteInput() {
+        Context context = getInstrumentation().getTargetContext();
+        NotificationBuilderBase notificationBuilder = new CustomNotificationBuilder(
+                context).addTextAction(null, "Action Title", null, "Placeholder");
+
+        Notification notification = notificationBuilder.build();
+
+        assertEquals(1, notification.actions.length);
+        assertEquals("Action Title", notification.actions[0].title);
+        assertNotNull(notification.actions[0].getRemoteInputs());
+        assertEquals(1, notification.actions[0].getRemoteInputs().length);
+        assertEquals("Placeholder", notification.actions[0].getRemoteInputs()[0].getLabel());
+    }
+
     private static void assertLargeNotificationIconAsExpected(
             Context context, Notification notification, Bitmap expectedIcon) {
         // 1. Check large icon property on the notification.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index d22cfc5..110831c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -17,7 +18,9 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.infobar.InfoBar;
@@ -228,6 +231,32 @@
     }
 
     /**
+     * Verifies that specifying a notification action with type: 'text' results in a notification
+     * with a remote input on the action.
+     */
+    @CommandLineFlags.Add("enable-experimental-web-platform-features")
+    @MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT_WATCH)
+    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) // RemoteInputs were only added in KITKAT_WATCH.
+    @MediumTest
+    @Feature({"Browser", "Notifications"})
+    public void testNotificationWithTextAction() throws Exception {
+        setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW);
+
+        Notification notification = showAndGetNotification("MyNotification", "{ "
+                        + " actions: [{action: 'myAction', title: 'reply', type: 'text',"
+                        + " placeholder: 'hi' }]}");
+
+        // The specified action should be present, as well as a default settings action.
+        assertEquals(2, notification.actions.length);
+
+        Notification.Action action = notification.actions[0];
+        assertEquals("reply", action.title);
+        assertNotNull(notification.actions[0].getRemoteInputs());
+        assertEquals(1, action.getRemoteInputs().length);
+        assertEquals("hi", action.getRemoteInputs()[0].getLabel());
+    }
+
+    /**
      * Verifies that the ONLY_ALERT_ONCE flag is not set when renotify is true.
      */
     @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java
index 24c6ac5..2a5f2ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.notifications;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -16,6 +17,7 @@
 import android.text.SpannableStringBuilder;
 
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.util.UrlUtilities;
 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
@@ -76,8 +78,8 @@
                 .setVibrate(new long[] {100L})
                 .setContentIntent(outContentAndDeleteIntents[0])
                 .setDeleteIntent(outContentAndDeleteIntents[1])
-                .addAction(actionIcon, "button 1", null /* intent */)
-                .addAction(actionIcon, "button 2", null /* intent */)
+                .addButtonAction(actionIcon, "button 1", null /* intent */)
+                .addButtonAction(actionIcon, "button 2", null /* intent */)
                 .addSettingsAction(0 /* iconId */, "settings", null /* intent */);
     }
 
@@ -184,4 +186,22 @@
             assertTrue(expected.sameAs(result));
         }
     }
+
+    @MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT_WATCH)
+    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) // RemoteInputs were only added in KITKAT_WATCH.
+    @SmallTest
+    @Feature({"Browser", "Notifications"})
+    public void testAddTextActionSetsRemoteInput() {
+        Context context = getInstrumentation().getTargetContext();
+        NotificationBuilderBase notificationBuilder = new StandardNotificationBuilder(
+                context).addTextAction(null, "Action Title", null, "Placeholder");
+
+        Notification notification = notificationBuilder.build();
+
+        assertEquals(1, notification.actions.length);
+        assertEquals("Action Title", notification.actions[0].title);
+        assertNotNull(notification.actions[0].getRemoteInputs());
+        assertEquals(1, notification.actions[0].getRemoteInputs().length);
+        assertEquals("Placeholder", notification.actions[0].getRemoteInputs()[0].getLabel());
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index 1cc0028..956e6f0 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -4,10 +4,12 @@
 
 package org.chromium.chrome.browser.ntp.cards;
 
+import static org.chromium.base.test.util.Matchers.greaterThanOrEqualTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -31,7 +33,6 @@
 import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCardLayout;
 import org.chromium.chrome.browser.ntp.snippets.FakeSuggestionsSource;
 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
-import org.chromium.chrome.browser.ntp.snippets.SectionHeader;
 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
 import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver;
@@ -86,47 +87,43 @@
      * expressed as a sequence of calls to the {@link #expect} methods.
      */
     private static class ItemsMatcher { // TODO(pke): Find better name.
-        private final List<NewTabPageItem> mItems;
+        private final NewTabPageAdapter mAdapter;
         private int mCurrentIndex;
 
-        public ItemsMatcher(List<NewTabPageItem> items) {
-            mItems = items;
+        public ItemsMatcher(NewTabPageAdapter adapter, int startingIndex) {
+            mAdapter = adapter;
+            mCurrentIndex = startingIndex;
         }
 
-        public void expect(Class<? extends NewTabPageItem> itemType) {
-            if (mCurrentIndex >= mItems.size()) {
-                fail("Expected another " + itemType.getSimpleName() + " after the following items: "
-                        + mItems);
+        public void expect(@NewTabPageItem.ViewType int expectedItemType) {
+            if (mCurrentIndex >= mAdapter.getItemCount()) {
+                fail("Expected item of type " + expectedItemType + " but encountered end of list");
             }
-            NewTabPageItem item = mItems.get(mCurrentIndex);
-            if (!itemType.isInstance(item)) {
-                fail("Expected the element at position " + mCurrentIndex + " to be a "
-                        + itemType.getSimpleName() + " instead of a "
-                        + item.getClass().getSimpleName() + "; full list: " + mItems);
-            }
+            @NewTabPageItem.ViewType int itemType = mAdapter.getItemViewType(mCurrentIndex);
+            assertEquals("Type mismatch at position " + mCurrentIndex, expectedItemType, itemType);
             mCurrentIndex++;
         }
 
         public void expect(SectionDescriptor descriptor) {
-            expect(SectionHeader.class);
+            expect(NewTabPageItem.VIEW_TYPE_HEADER);
             if (descriptor.mStatusCard) {
-                expect(StatusItem.class);
+                expect(NewTabPageItem.VIEW_TYPE_STATUS);
                 if (descriptor.mMoreButton) {
-                    expect(ActionItem.class);
+                    expect(NewTabPageItem.VIEW_TYPE_ACTION);
                 }
-                expect(ProgressItem.class);
+                expect(NewTabPageItem.VIEW_TYPE_PROGRESS);
             } else {
                 for (int i = 1; i <= descriptor.mNumSuggestions; i++) {
-                    expect(SnippetArticle.class);
+                    expect(NewTabPageItem.VIEW_TYPE_SNIPPET);
                 }
                 if (descriptor.mMoreButton) {
-                    expect(ActionItem.class);
+                    expect(NewTabPageItem.VIEW_TYPE_ACTION);
                 }
             }
         }
 
-        public void expectFinished() {
-            assertEquals(mItems.size(), mCurrentIndex);
+        public void expectPosition(int expectedPosition) {
+            assertEquals(expectedPosition, mCurrentIndex);
         }
     }
 
@@ -137,9 +134,10 @@
      * @param itemGroup The items from the adapter.
      */
     private void assertMatches(SectionDescriptor descriptor, ItemGroup itemGroup) {
-        ItemsMatcher matcher = new ItemsMatcher(itemGroup.getItems());
+        int offset = mAdapter.getGroupPositionOffset(itemGroup);
+        ItemsMatcher matcher = new ItemsMatcher(mAdapter, offset);
         matcher.expect(descriptor);
-        matcher.expectFinished();
+        matcher.expectPosition(offset + itemGroup.getItems().size());
     }
 
     /**
@@ -149,14 +147,14 @@
      *                    the UI.
      */
     private void assertItemsFor(SectionDescriptor... descriptors) {
-        ItemsMatcher matcher = new ItemsMatcher(mAdapter.getItems());
-        matcher.expect(AboveTheFoldItem.class);
+        ItemsMatcher matcher = new ItemsMatcher(mAdapter, 0);
+        matcher.expect(NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD);
         for (SectionDescriptor descriptor : descriptors) matcher.expect(descriptor);
         if (descriptors.length > 0) {
-            matcher.expect(Footer.class);
-            matcher.expect(SpacingItem.class);
+            matcher.expect(NewTabPageItem.VIEW_TYPE_FOOTER);
+            matcher.expect(NewTabPageItem.VIEW_TYPE_SPACING);
         }
-        matcher.expectFinished();
+        matcher.expectPosition(mAdapter.getItemCount());
     }
 
     /**
@@ -235,22 +233,25 @@
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions);
 
-        List<NewTabPageItem> loadedItems = new ArrayList<>(mAdapter.getItems());
+        int numItems = mAdapter.getItemCount();
+
+        // From the loaded items, cut out aboveTheFold and header from the front,
+        // and footer and bottom spacer from the back.
         assertEquals(NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD, mAdapter.getItemViewType(0));
         assertEquals(NewTabPageItem.VIEW_TYPE_HEADER, mAdapter.getItemViewType(1));
-        // From the loadedItems, cut out aboveTheFold and header from the front,
-        // and footer and bottom spacer from the back.
-        assertEquals(suggestions, loadedItems.subList(2, loadedItems.size() - 2));
-        assertEquals(
-                NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(loadedItems.size() - 2));
-        assertEquals(
-                NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(loadedItems.size() - 1));
+        assertArticlesEqual(suggestions, 2, numItems - 2);
+        assertEquals(NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(numItems - 2));
+        assertEquals(NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(numItems - 1));
 
         // The adapter should ignore any new incoming data.
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES,
                 Arrays.asList(new SnippetArticle[] {new SnippetArticle(0, "foo", "title1", "pub1",
                         "txt1", "foo", "bar", 0, 0, 0, ContentSuggestionsCardLayout.FULL_CARD)}));
-        assertEquals(loadedItems, mAdapter.getItems());
+        assertEquals(NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD, mAdapter.getItemViewType(0));
+        assertEquals(NewTabPageItem.VIEW_TYPE_HEADER, mAdapter.getItemViewType(1));
+        assertArticlesEqual(suggestions, 2, numItems - 2);
+        assertEquals(NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(numItems - 2));
+        assertEquals(NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(numItems - 1));
     }
 
     /**
@@ -275,22 +276,26 @@
         List<SnippetArticle> suggestions = createDummySuggestions(5);
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, suggestions);
-        List<NewTabPageItem> loadedItems = new ArrayList<>(mAdapter.getItems());
+
+        int numItems = mAdapter.getItemCount();
+
+        // From the loaded items, cut out aboveTheFold and header from the front,
+        // and footer and bottom spacer from the back.
         assertEquals(NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD, mAdapter.getItemViewType(0));
         assertEquals(NewTabPageItem.VIEW_TYPE_HEADER, mAdapter.getItemViewType(1));
-        // From the loadedItems, cut out aboveTheFold and header from the front,
-        // and footer and bottom spacer from the back.
-        assertEquals(suggestions, loadedItems.subList(2, loadedItems.size() - 2));
-        assertEquals(
-                NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(loadedItems.size() - 2));
-        assertEquals(
-                NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(loadedItems.size() - 1));
+        assertArticlesEqual(suggestions, 2, numItems - 2);
+        assertEquals(NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(numItems - 2));
+        assertEquals(NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(numItems - 1));
 
         // The adapter should ignore any new incoming data.
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES,
                 Arrays.asList(new SnippetArticle[] {new SnippetArticle(0, "foo", "title1", "pub1",
                         "txt1", "foo", "bar", 0, 0, 0, ContentSuggestionsCardLayout.FULL_CARD)}));
-        assertEquals(loadedItems, mAdapter.getItems());
+        assertEquals(NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD, mAdapter.getItemViewType(0));
+        assertEquals(NewTabPageItem.VIEW_TYPE_HEADER, mAdapter.getItemViewType(1));
+        assertArticlesEqual(suggestions, 2, numItems - 2);
+        assertEquals(NewTabPageItem.VIEW_TYPE_FOOTER, mAdapter.getItemViewType(numItems - 2));
+        assertEquals(NewTabPageItem.VIEW_TYPE_SPACING, mAdapter.getItemViewType(numItems - 1));
     }
 
     /**
@@ -365,7 +370,9 @@
     @Feature({"Ntp"})
     public void testProgressIndicatorDisplay() {
         int progressPos = mAdapter.getLastContentItemPosition() - 1;
-        ProgressItem progress = (ProgressItem) mAdapter.getItems().get(progressPos);
+        SuggestionsSection section = (SuggestionsSection) mAdapter.getGroup(progressPos);
+        List<NewTabPageItem> items = section.getItems();
+        ProgressItem progress = (ProgressItem) items.get(items.size() - 1);
 
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.INITIALIZING);
         assertTrue(progress.isVisible());
@@ -435,6 +442,7 @@
         assertItemsFor();
     }
 
+    /** Tests whether a section stays visible if empty, if required. */
     @Test
     @Feature({"Ntp"})
     public void testSectionVisibleIfEmpty() {
@@ -552,6 +560,13 @@
         assertItemsFor(sectionWithStatusCard());
     }
 
+    private void assertArticlesEqual(List<SnippetArticle> articles, int start, int end) {
+        assertThat(mAdapter.getItemCount(), greaterThanOrEqualTo(end));
+        for (int i = start; i < end; i++) {
+            assertEquals(articles.get(i - start), mAdapter.getSuggestionAt(i));
+        }
+    }
+
     /**
      * Tests that invalidated suggestions are immediately removed.
      */
@@ -562,11 +577,11 @@
         mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AVAILABLE);
         mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, articles);
         assertItemsFor(section(3));
-        assertEquals(articles, mAdapter.getItems().subList(2, 5));
+        assertArticlesEqual(articles, 2, 5);
 
         SnippetArticle removed = articles.remove(1);
         mSource.fireSuggestionInvalidated(KnownCategories.ARTICLES, removed.mId);
-        assertEquals(articles, mAdapter.getItems().subList(2, 4));
+        assertArticlesEqual(articles, 2, 4);
     }
 
     /**
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 1a604c7..28c1112b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12144,6 +12144,9 @@
         <message name="IDS_FLAGS_ENABLE_WEB_NOTIFICATION_CUSTOM_LAYOUTS_DESCRIPTION" desc="Description for the flag to enable custom layouts for Web Notifications.">
           Enable custom layouts for Web Notifications. They will have subtle layout improvements that are otherwise not possible.
         </message>
+        <message name="IDS_NOTIFICATION_REPLY_PLACEHOLDER" desc="Placeholder text shown in the text box before any text is entered when replying directly to a notification.">
+          Send message
+        </message>
       </if>
 
       <!-- Mac AppleScript -->
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index 95429af..66af8897 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -217,7 +217,7 @@
 }
 
 void UiScene::AddAnimationFromDict(const base::DictionaryValue& dict,
-                                   int64_t time) {
+                                   int64_t time_in_micro) {
   int animation_id;
   int element_id;
   Animation::Property property;
@@ -249,7 +249,7 @@
     ParseEndpointToFloats(property, *from_dict, &from);
   }
 
-  int64_t start = time + (long)(start_time_ms * 1000.0);
+  int64_t start = time_in_micro + (long)(start_time_ms * 1000.0);
   int64_t duration = duration_ms * 1000.0;
 
   ContentRectangle* element = GetUiElementById(element_id);
@@ -273,8 +273,9 @@
   }
 }
 
-void UiScene::HandleCommands(const base::ListValue& commands, int64_t time) {
-  for (auto& item : commands) {
+void UiScene::HandleCommands(const base::ListValue* commands,
+                             int64_t time_in_micro) {
+  for (auto& item : *commands) {
     base::DictionaryValue* dict;
     CHECK(item->GetAsDictionary(&dict));
 
@@ -297,7 +298,7 @@
         break;
       }
       case Command::ADD_ANIMATION:
-        AddAnimationFromDict(*data, time);
+        AddAnimationFromDict(*data, time_in_micro);
         break;
       case Command::REMOVE_ANIMATION: {
         int element_id, animation_id;
@@ -310,10 +311,10 @@
   }
 }
 
-void UiScene::UpdateTransforms(float screen_tilt, int64_t time) {
+void UiScene::UpdateTransforms(float screen_tilt, int64_t time_in_micro) {
   // Process all animations before calculating object transforms.
   for (auto& element : ui_elements_) {
-    element->Animate(time);
+    element->Animate(time_in_micro);
   }
   for (auto& element : ui_elements_) {
     element->transform.MakeIdentity();
@@ -341,6 +342,11 @@
 
 UiScene::~UiScene() = default;
 
+int64_t UiScene::TimeInMicroseconds() {
+  return std::chrono::duration_cast<std::chrono::microseconds>(
+      std::chrono::steady_clock::now().time_since_epoch()).count();
+}
+
 void UiScene::ApplyRecursiveTransforms(const ContentRectangle& element,
                                        ReversibleTransform* transform) {
   transform->Scale(element.scale.x, element.scale.y, element.scale.z);
diff --git a/chrome/browser/android/vr_shell/ui_scene.h b/chrome/browser/android/vr_shell/ui_scene.h
index 905fbe4..a5a33d8 100644
--- a/chrome/browser/android/vr_shell/ui_scene.h
+++ b/chrome/browser/android/vr_shell/ui_scene.h
@@ -41,7 +41,8 @@
   void AddAnimation(int element_id, std::unique_ptr<Animation>& animation);
 
   // Add an animation according to a dictionary passed from the UI HTML.
-  void AddAnimationFromDict(const base::DictionaryValue& dict, int64_t time);
+  void AddAnimationFromDict(const base::DictionaryValue& dict,
+                            int64_t time_in_micro);
 
   // Remove |animation_id| from element |element_id|.
   void RemoveAnimation(int element_id, int animation_id);
@@ -49,15 +50,18 @@
   // Update the positions of all elements in the scene, according to active
   // animations, desired screen tilt and time.  The units of time are
   // arbitrary, but must match the unit used in animations.
-  void UpdateTransforms(float screen_tilt, int64_t time);
+  void UpdateTransforms(float screen_tilt, int64_t time_in_micro);
 
   // Handle a batch of commands passed from the UI HTML.
-  void HandleCommands(const base::ListValue& commands, int64_t time);
+  void HandleCommands(const base::ListValue* commands, int64_t time_in_micro);
 
   const std::vector<std::unique_ptr<ContentRectangle>>& GetUiElements() const;
 
   ContentRectangle* GetUiElementById(int element_id);
 
+  // Return a monotonic time in microseconds for coordinating animations.
+  static int64_t TimeInMicroseconds();
+
  private:
   void ApplyRecursiveTransforms(const ContentRectangle& element,
                                 ReversibleTransform* transform);
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc
index 8a6b659..6ee0767 100644
--- a/chrome/browser/android/vr_shell/vr_shell.cc
+++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -275,14 +275,14 @@
   if (webvr_mode_) {
     DrawWebVr();
   } else {
-    DrawVrShell(target_time.monotonic_system_time_nanos);
+    DrawVrShell();
   }
 
   frame.Unbind();
   frame.Submit(*buffer_viewport_list_, head_pose_);
 }
 
-void VrShell::DrawVrShell(int64_t time) {
+void VrShell::DrawVrShell() {
   float screen_tilt = desktop_screen_tilt_ * M_PI / 180.0f;
 
   gvr::Vec3f headPos = getTranslation(head_pose_);
@@ -299,8 +299,10 @@
 
   desktop_plane_->translation = desktop_position_;
 
+  HandleQueuedTasks();
+
   // Update the render position of all UI elements (including desktop).
-  scene_.UpdateTransforms(screen_tilt, time);
+  scene_.UpdateTransforms(screen_tilt, UiScene::TimeInMicroseconds());
 
   UpdateController();
 
@@ -366,8 +368,13 @@
       copy_rect = {0, 0, 1, 1};
       texture_handle = content_texture_id_;
     } else {
-      // TODO(cjgrant): Populate UI texture and allow rendering.
-      continue;
+      copy_rect.x = static_cast<float>(rect->copy_rect.x) / ui_tex_width_;
+      copy_rect.y = static_cast<float>(rect->copy_rect.y) / ui_tex_height_;
+      copy_rect.width = static_cast<float>(rect->copy_rect.width) /
+          ui_tex_width_;
+      copy_rect.height = static_cast<float>(rect->copy_rect.height) /
+          ui_tex_height_;
+      texture_handle = ui_texture_id_;
     }
 
     vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
@@ -507,6 +514,32 @@
   ui_compositor_->SurfaceChanged((int)width, (int)height, surface);
 }
 
+UiScene* VrShell::GetScene() {
+  return &scene_;
+}
+
+void VrShell::QueueTask(base::Callback<void()>& callback) {
+  base::AutoLock lock(task_queue_lock_);
+  task_queue_.push(callback);
+}
+
+void VrShell::HandleQueuedTasks() {
+  // To protect a stream of tasks from blocking rendering indefinitely,
+  // process only the number of tasks present when first checked.
+  std::vector<base::Callback<void()>> tasks;
+  {
+    base::AutoLock lock(task_queue_lock_);
+    const size_t count = task_queue_.size();
+    for (size_t i = 0; i < count; i++) {
+      tasks.push_back(task_queue_.front());
+      task_queue_.pop();
+    }
+  }
+  for (auto &task : tasks) {
+    task.Run();
+  }
+}
+
 // ----------------------------------------------------------------------------
 // Native JNI methods
 // ----------------------------------------------------------------------------
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h
index 518ec83..0f18ae2e 100644
--- a/chrome/browser/android/vr_shell/vr_shell.h
+++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -12,6 +12,7 @@
 #include "base/android/jni_weak_ref.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
 #include "chrome/browser/android/vr_shell/ui_elements.h"
 #include "chrome/browser/android/vr_shell/ui_scene.h"
 #include "device/vr/android/gvr/gvr_delegate.h"
@@ -62,6 +63,7 @@
 
   // html/js UI hooks.
   static base::WeakPtr<VrShell> GetWeakPtr();
+  UiScene* GetScene();
   void OnDomContentsLoaded();
   void SetUiTextureSize(int width, int height);
 
@@ -84,10 +86,14 @@
       jint height,
       const base::android::JavaParamRef<jobject>& surface);
 
+  // Called from non-render thread to queue a callback onto the render thread.
+  // The render thread checks for callbacks and processes them between frames.
+  void QueueTask(base::Callback<void()>& callback);
+
  private:
   virtual ~VrShell();
   void LoadUIContent();
-  void DrawVrShell(int64_t time);
+  void DrawVrShell();
   void DrawEye(const gvr::Mat4f& view_matrix,
                const gvr::BufferViewport& params);
   void DrawContentRect();
@@ -97,6 +103,8 @@
 
   void UpdateController();
 
+  void HandleQueuedTasks();
+
   // samplerExternalOES texture data for UI content image.
   jint ui_texture_id_ = 0;
   // samplerExternalOES texture data for main content image.
@@ -124,6 +132,9 @@
   gvr::Sizei render_size_;
   float cursor_distance_;
 
+  std::queue<base::Callback<void()>> task_queue_;
+  base::Lock task_queue_lock_;
+
   std::unique_ptr<VrCompositor> content_compositor_;
   content::ContentViewCore* content_cvc_;
   std::unique_ptr<VrCompositor> ui_compositor_;
diff --git a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
index fbb3fe3..b43173d 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_management_policy_provider.cc
@@ -37,6 +37,7 @@
     "ngjnkanfphagcaokhjecbgkboelgfcnf",  // Print button
     "gbchcmhmhahfdphkhkmpfmihenigjmpp",  // Chrome Remote Desktop
     "cjanmonomjogheabiocdamfpknlpdehm",  // HP printer driver
+    "ioofdkhojeeimmagbjbknkejkgbphdfl",  // RICOH Print for Chrome
     "pmnllmkmjilbojkpgplbdmckghmaocjh",  // Scan app by François Beaufort
     "khpfeaanjngmcnplbdlpegiifgpfgdco",  // Smart Card Connector App
     "haeblkpifdemlfnkogkipmghfcbonief",  // Charismathics Smart Card Middleware
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest.cc b/chrome/browser/media/webrtc/webrtc_browsertest.cc
index f2abe3de..10ead6a 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest.cc
@@ -49,6 +49,12 @@
 
     // Flag used by TestWebAudioMediaStream to force garbage collection.
     command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc");
+
+    // Flag used by |RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise|.
+    // TODO(hbos): Remove this when bug crbug.com/627816 is resolved (when this
+    // flag is removed).
+    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                                    "RTCPeerConnectionNewGetStats");
   }
 
   void RunsAudioVideoWebRTCCallInTwoTabs(
@@ -198,13 +204,25 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest,
-                       RunsAudioVideoWebRTCCallInTwoTabsGetStats) {
+                       RunsAudioVideoWebRTCCallInTwoTabsGetStatsCallback) {
   StartServerAndOpenTabs();
   SetupPeerconnectionWithLocalStream(left_tab_);
   SetupPeerconnectionWithLocalStream(right_tab_);
   NegotiateCall(left_tab_, right_tab_);
 
-  VerifyStatsGenerated(left_tab_);
+  VerifyStatsGeneratedCallback(left_tab_);
+
+  DetectVideoAndHangUp();
+}
+
+IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest,
+                       RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise) {
+  StartServerAndOpenTabs();
+  SetupPeerconnectionWithLocalStream(left_tab_);
+  SetupPeerconnectionWithLocalStream(right_tab_);
+  NegotiateCall(left_tab_, right_tab_);
+
+  VerifyStatsGeneratedPromise(left_tab_);
 
   DetectVideoAndHangUp();
 }
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index 6c3bcd69..107b49f6 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -455,10 +455,17 @@
   EXPECT_EQ("ok-generated-and-cloned", ExecuteJavascript(javascript, tab));
 }
 
-void WebRtcTestBase::VerifyStatsGenerated(content::WebContents* tab) const {
+void WebRtcTestBase::VerifyStatsGeneratedCallback(
+    content::WebContents* tab) const {
   EXPECT_EQ("ok-got-stats", ExecuteJavascript("verifyStatsGenerated()", tab));
 }
 
+void WebRtcTestBase::VerifyStatsGeneratedPromise(
+    content::WebContents* tab) const {
+  EXPECT_EQ("ok-got-stats",
+            ExecuteJavascript("verifyStatsGeneratedPromise()", tab));
+}
+
 void WebRtcTestBase::SetDefaultVideoCodec(
     content::WebContents* tab,
     const std::string& video_codec) const {
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.h b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
index c33b2f5..571e1a1 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.h
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.h
@@ -161,7 +161,8 @@
   void GenerateAndCloneCertificate(content::WebContents* tab,
                                    const std::string& keygen_algorithm) const;
 
-  void VerifyStatsGenerated(content::WebContents* tab) const;
+  void VerifyStatsGeneratedCallback(content::WebContents* tab) const;
+  void VerifyStatsGeneratedPromise(content::WebContents* tab) const;
 
   // Change the default video codec in the offer SDP.
   void SetDefaultVideoCodec(content::WebContents* tab,
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc
index 9c3075a..5b279fe 100644
--- a/chrome/browser/notifications/notification_platform_bridge_android.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/native_notification_display_service.h"
 #include "chrome/browser/notifications/notification.h"
@@ -218,12 +219,34 @@
   if (!badge_bitmap.drawsNothing())
     badge = gfx::ConvertToJavaBitmap(&badge_bitmap);
 
+  // TODO(crbug.com/650302): Combine these action_* vectors into a single vector
+  // of objects.
   std::vector<base::string16> action_titles_vector;
-  for (const message_center::ButtonInfo& button : notification.buttons())
+  std::vector<base::string16> action_types_vector;
+  std::vector<base::string16> action_placeholders_vector;
+  for (const message_center::ButtonInfo& button : notification.buttons()) {
     action_titles_vector.push_back(button.title);
+    action_placeholders_vector.push_back(button.placeholder);
+    base::string16 type;
+    switch (button.type) {
+      case message_center::ButtonType::BUTTON:
+        type = base::ASCIIToUTF16("button");
+        break;
+      case message_center::ButtonType::TEXT:
+        type = base::ASCIIToUTF16("text");
+        break;
+    }
+    action_types_vector.push_back(std::move(type));
+  }
   ScopedJavaLocalRef<jobjectArray> action_titles =
       base::android::ToJavaArrayOfStrings(env, action_titles_vector);
 
+  ScopedJavaLocalRef<jobjectArray> action_types =
+      base::android::ToJavaArrayOfStrings(env, action_types_vector);
+
+  ScopedJavaLocalRef<jobjectArray> action_placeholders =
+      base::android::ToJavaArrayOfStrings(env, action_placeholders_vector);
+
   ScopedJavaLocalRef<jobjectArray> action_icons =
       ConvertToJavaBitmaps(notification.buttons());
 
@@ -238,7 +261,7 @@
       tag, webapk_package, title, body, image, notification_icon, badge,
       vibration_pattern, notification.timestamp().ToJavaTime(),
       notification.renotify(), notification.silent(), action_titles,
-      action_icons);
+      action_icons, action_types, action_placeholders);
 
   regenerated_notification_infos_[notification_id] =
       RegeneratedNotificationInfo(origin_url.spec(), notification.tag(),
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index 7b1472a..048fe0a 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -38,7 +39,9 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/common/notification_resources.h"
 #include "content/public/common/platform_notification_data.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/message_center/notification.h"
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
 #include "url/url_constants.h"
@@ -443,11 +446,25 @@
   // Developer supplied action buttons.
   std::vector<message_center::ButtonInfo> buttons;
   for (size_t i = 0; i < notification_data.actions.size(); i++) {
-    message_center::ButtonInfo button(notification_data.actions[i].title);
+    content::PlatformNotificationAction const& action =
+        notification_data.actions[i];
+    message_center::ButtonInfo button(action.title);
     // TODO(peter): Handle different screen densities instead of always using
     // the 1x bitmap - crbug.com/585815.
     button.icon =
         gfx::Image::CreateFrom1xBitmap(notification_resources.action_icons[i]);
+    button.placeholder =
+        action.placeholder.is_null()
+            ? l10n_util::GetStringUTF16(IDS_NOTIFICATION_REPLY_PLACEHOLDER)
+            : action.placeholder.string();
+    switch (action.type) {
+      case content::PLATFORM_NOTIFICATION_ACTION_TYPE_BUTTON:
+        button.type = message_center::ButtonType::BUTTON;
+        break;
+      case content::PLATFORM_NOTIFICATION_ACTION_TYPE_TEXT:
+        button.type = message_center::ButtonType::TEXT;
+        break;
+    }
     buttons.push_back(button);
   }
   notification.set_buttons(buttons);
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 31f18936..437ae9f 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -636,6 +636,12 @@
 
   ChromePasswordManagerClient* instance =
       ChromePasswordManagerClient::FromWebContents(web_contents);
-  DCHECK(instance);
+
+  // Try to bind to the driver, but if driver is not available for this render
+  // frame host, the request will be just dropped. This will cause the message
+  // pipe to be closed, which will raise a connection error on the peer side.
+  if (!instance)
+    return;
+
   instance->credential_manager_impl_.BindRequest(std::move(request));
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 82edfffa..b47c576 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -549,3 +549,21 @@
       SerializedNavigationEntry::HAS_PASSWORD_FIELD,
       GetPasswordStateFromNavigation(*controller().GetLastCommittedEntry()));
 }
+
+// Handle missing ChromePasswordManagerClient instance in BindCredentialManager
+// gracefully.
+TEST_F(ChromePasswordManagerClientTest, BindCredentialManager_MissingInstance) {
+  // Create a WebContent without tab helpers.
+  std::unique_ptr<content::WebContents> web_contents(
+      content::WebContents::Create(
+          content::WebContents::CreateParams(profile())));
+  // In particular, this WebContent should not have the
+  // ChromePasswordManagerClient.
+  ASSERT_FALSE(
+      ChromePasswordManagerClient::FromWebContents(web_contents.get()));
+
+  // This call should not crash.
+  ChromePasswordManagerClient::BindCredentialManager(
+      web_contents->GetMainFrame(),
+      password_manager::mojom::CredentialManagerRequest());
+}
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js
index aacdb534..7c14b72f 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.js
@@ -14,10 +14,9 @@
    * @const
    */
   var XAnchoring = Object.freeze({
-    'XLEFT': 0,
-    'XRIGHT': 1,
-    'XCENTER': 2,
-    'XNONE': 3
+    'XNONE': 0,
+    'XLEFT': 1,
+    'XRIGHT': 2
   });
 
   /**
@@ -26,10 +25,9 @@
    * @const
    */
   var YAnchoring = Object.freeze({
-    'YTOP': 0,
-    'YBOTTOM': 1,
-    'YCENTER': 2,
-    'YNONE': 3
+    'YNONE': 0,
+    'YTOP': 1,
+    'YBOTTOM': 2
   });
 
   /**
@@ -58,6 +56,19 @@
   });
 
   /**
+   * Enumeration of scene update commands.
+   * @enum {number}
+   * @const
+   */
+  var Command = Object.freeze({
+    'ADD_ELEMENT': 0,
+    'UPDATE_ELEMENT': 1,
+    'REMOVE_ELEMENT': 2,
+    'ADD_ANIMATION': 3,
+    'REMOVE_ANIMATION': 4
+  });
+
+  /**
    * @type {number} Id generator.
    */
   var idIndex = 1;
@@ -80,7 +91,6 @@
       this.size = { x: metersX, y: metersY };
       this.xAnchoring = XAnchoring.XNONE;
       this.yAnchoring = YAnchoring.YNONE;
-      this.anchorZ = false;
       this.translation = { x: 0, y: 0, z: 0 };
       this.orientationAxisAngle = { x: 0, y: 0, z: 0, a: 0 };
       this.rotationAxisAngle = { x: 0, y: 0, z: 0, a: 0 };
@@ -104,15 +114,13 @@
     }
 
     /**
-     * Anchoring allows a rectangle to be positioned relative to the content
-     * window.  X and Y values should be XAnchoring and YAnchoring elements.
-     * anchorZ is a boolean.
-     * Example: rect.setAnchoring(XAnchoring.XCENTER, YAnchoring.YBOTTOM, true);
+     * Anchoring allows a rectangle to be positioned relative to the edge of
+     * content window.  Values should be XAnchoring and YAnchoring elements.
+     * Example: rect.setAnchoring(XAnchoring.XNONE, YAnchoring.YBOTTOM);
      */
     setAnchoring(x, y, z) {
       this.xAnchoring = x;
       this.yAnchoring = y;
-      this.anchorZ = z;
     }
   };
 
@@ -191,7 +199,8 @@
 
       // Add a UI rectangle for the button.
       var el = new UiElement(50 * i, 200, 50, 50, buttonWidth, buttonHeight);
-      el.setAnchoring(XAnchoring.XCENTER, YAnchoring.YBOTTOM, true);
+      el.parentId = 0;
+      el.setAnchoring(XAnchoring.XNONE, YAnchoring.YBOTTOM);
       el.setTranslation(buttonStartPosition + buttonSpacing * i, -0.3, 0.0);
       var id = idIndex++;
       addMesh(id, el);
@@ -216,12 +225,30 @@
     chrome.send('domLoaded', [window.innerWidth, window.innerHeight]);
   }
 
-  function addAnimations(animations) {
-    chrome.send('addAnimations', animations);
+  function addMesh(id, mesh) {
+    mesh.id = id;
+    chrome.send('updateScene', [{
+      'type': Command.ADD_ELEMENT,
+      'data': mesh
+    }]);
   }
 
-  function addMesh(id, mesh) {
-    chrome.send('addMesh', [id, mesh]);
+  function removeMesh(id) {
+    chrome.send('updateScene', [{
+      'type': Command.REMOVE_ELEMENT,
+      'data': {'id': id}
+    }]);
+  }
+
+  function addAnimations(animations) {
+    var commands = [];
+    for (var i = 0; i < animations.length; i++) {
+      commands.push({
+        'type': Command.ADD_ANIMATION,
+        'data': animations[i]
+      });
+    }
+    chrome.send('updateScene', commands);
   }
 
   return {
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc
index 4b4b3ec..a4d28369 100644
--- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc
+++ b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/values.h"
 #include "chrome/browser/android/vr_shell/vr_shell.h"
 #include "content/public/browser/web_ui.h"
@@ -23,17 +24,11 @@
       "domLoaded", base::Bind(&VrShellUIMessageHandler::HandleDomLoaded,
                               base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "addMesh", base::Bind(&VrShellUIMessageHandler::HandleAddMesh,
+      "updateScene", base::Bind(&VrShellUIMessageHandler::HandleUpdateScene,
                             base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "removeMesh", base::Bind(&VrShellUIMessageHandler::HandleRemoveMesh,
-                               base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "doAction", base::Bind(&VrShellUIMessageHandler::HandleDoAction,
                              base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
-      "addAnimations", base::Bind(&VrShellUIMessageHandler::HandleAddAnimations,
-                                  base::Unretained(this)));
 }
 
 void VrShellUIMessageHandler::HandleDomLoaded(const base::ListValue* args) {
@@ -50,16 +45,16 @@
   vr_shell_->SetUiTextureSize(width, height);
 }
 
-void VrShellUIMessageHandler::HandleAddMesh(const base::ListValue* args) {
-  NOTIMPLEMENTED();
-}
+void VrShellUIMessageHandler::HandleUpdateScene(const base::ListValue* args) {
+  if (!vr_shell_)
+    return;
 
-void VrShellUIMessageHandler::HandleRemoveMesh(const base::ListValue* args) {
-  NOTIMPLEMENTED();
-}
-
-void VrShellUIMessageHandler::HandleAddAnimations(const base::ListValue* args) {
-  NOTIMPLEMENTED();
+  // Copy the update instructions and handle them on the render thread.
+  auto cb = base::Bind(&vr_shell::UiScene::HandleCommands,
+                       base::Unretained(vr_shell_->GetScene()),
+                       base::Owned(args->CreateDeepCopy().release()),
+                       vr_shell::UiScene::TimeInMicroseconds());
+  vr_shell_->QueueTask(cb);
 }
 
 void VrShellUIMessageHandler::HandleDoAction(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h
index 5a87fa2a..6fd3370c 100644
--- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h
+++ b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h
@@ -29,9 +29,7 @@
   void RegisterMessages() override;
 
   void HandleDomLoaded(const base::ListValue* args);
-  void HandleAddMesh(const base::ListValue* args);
-  void HandleRemoveMesh(const base::ListValue* args);
-  void HandleAddAnimations(const base::ListValue* args);
+  void HandleUpdateScene(const base::ListValue* args);
   void HandleDoAction(const base::ListValue* args);
 
   base::WeakPtr<vr_shell::VrShell> vr_shell_;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2dd0e902..b01586da 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1360,7 +1360,6 @@
       "../browser/extensions/api/autofill_private/autofill_private_apitest.cc",
       "../browser/extensions/api/automation/automation_apitest.cc",
       "../browser/extensions/api/autotest_private/autotest_private_apitest.cc",
-      "../browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc",
       "../browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest_chromeos.cc",
       "../browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc",
       "../browser/extensions/api/bookmarks/bookmark_apitest.cc",
@@ -2659,6 +2658,10 @@
         ]
       }
     }
+
+    if (is_chromeos || (is_linux && use_dbus)) {
+      sources += [ "../browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc" ]
+    }
   }
 }
 
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
new file mode 100644
index 0000000..9258a36
--- /dev/null
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -0,0 +1,46 @@
+/**
+ * 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.
+ */
+
+// Public interface to tests. These are expected to be called with
+// ExecuteJavascript invocations from the browser tests and will return answers
+// through the DOM automation controller.
+
+/**
+ * Verifies that the promise-based |RTCPeerConnection.getStats| returns stats.
+ *
+ * Returns ok-got-stats on success.
+ */
+function verifyStatsGeneratedPromise() {
+  peerConnection_().getStats()
+    .then(function(report) {
+      if (report == null || report.size == 0)
+        throw new failTest('report is null or empty.');
+      // Sanity check that applies to all stats.
+      var ids = new Set();
+      report.forEach(function(stats) {
+        if (typeof(stats.id) !== 'string')
+          throw failTest('stats.id is not a string.');
+        if (ids.has(stats.id))
+          throw failTest('stats.id is not a unique identifier.');
+        ids.add(stats.id);
+        if (typeof(stats.timestamp) !== 'number' || stats.timestamp <= 0)
+          throw failTest('stats.timestamp is not a positive number.');
+        if (typeof(stats.type) !== 'string')
+          throw failTest('stats.type is not a string.');
+      });
+      // TODO(hbos): When the new stats collection API is more mature (and
+      // certainly before unflagging the new stats API) add a whitelist of
+      // allowed stats to prevent accidentally exposing stats to the web that
+      // are not in the spec and that verifies type information. Status at
+      // crbug.com/627816. Stats collection is tested in the WebRTC repo and
+      // automatically surfaced to Blink, but there should be a process of
+      // having to land a Blink CL in order to expose a new RTCStats dictionary.
+      returnToTest('ok-got-stats');
+    },
+    function(e) {
+      throw failTest('Promise was rejected: ' + e);
+    });
+}
diff --git a/chrome/test/data/webrtc/webrtc_jsep01_test.html b/chrome/test/data/webrtc/webrtc_jsep01_test.html
index 6928546..cc737be 100644
--- a/chrome/test/data/webrtc/webrtc_jsep01_test.html
+++ b/chrome/test/data/webrtc/webrtc_jsep01_test.html
@@ -10,6 +10,7 @@
   <script type="text/javascript" src="video_detector.js"></script>
   <script type="text/javascript" src="media_devices.js"></script>
   <script type="text/javascript" src="indexeddb.js"></script>
+  <script type="text/javascript" src="peerconnection_getstats.js"></script>
 </head>
 <body>
   <table border="0">
diff --git a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
index 7f75a2e..79b8718 100644
--- a/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
+++ b/components/ntp_snippets/bookmarks/bookmark_suggestions_provider.h
@@ -19,7 +19,7 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace ntp_snippets {
 
diff --git a/components/ntp_snippets/content_suggestions_provider.h b/components/ntp_snippets/content_suggestions_provider.h
index d479eec..bc568f5 100644
--- a/components/ntp_snippets/content_suggestions_provider.h
+++ b/components/ntp_snippets/content_suggestions_provider.h
@@ -16,7 +16,7 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace ntp_snippets {
 
diff --git a/components/ntp_snippets/content_suggestions_service.h b/components/ntp_snippets/content_suggestions_service.h
index a038fbfe..8c4a4f7 100644
--- a/components/ntp_snippets/content_suggestions_service.h
+++ b/components/ntp_snippets/content_suggestions_service.h
@@ -28,7 +28,7 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace ntp_snippets {
 
@@ -76,7 +76,7 @@
     virtual void ContentSuggestionsServiceShutdown() = 0;
 
    protected:
-    virtual ~Observer() {}
+    virtual ~Observer() = default;
   };
 
   enum State {
diff --git a/components/ntp_snippets/ntp_snippet.h b/components/ntp_snippets/ntp_snippet.h
index 01a81da..def1b88 100644
--- a/components/ntp_snippets/ntp_snippet.h
+++ b/components/ntp_snippets/ntp_snippet.h
@@ -17,7 +17,7 @@
 
 namespace base {
 class DictionaryValue;
-}
+}  // namespace base
 
 namespace ntp_snippets {
 
@@ -40,7 +40,7 @@
   // Creates a new snippet with the given |id|.
   // Public for testing only - create snippets using the Create* methods below.
   // TODO(treib): Make this private and add a CreateSnippetForTest?
-  NTPSnippet(const std::string& id);
+  explicit NTPSnippet(const std::string& id);
 
   ~NTPSnippet();
 
diff --git a/components/ntp_snippets/ntp_snippets_database.h b/components/ntp_snippets/ntp_snippets_database.h
index 8abc9809878b..dc5131c 100644
--- a/components/ntp_snippets/ntp_snippets_database.h
+++ b/components/ntp_snippets/ntp_snippets_database.h
@@ -20,7 +20,7 @@
 
 namespace base {
 class FilePath;
-}
+}  // namespace base
 
 namespace ntp_snippets {
 
diff --git a/components/ntp_snippets/ntp_snippets_fetcher.h b/components/ntp_snippets/ntp_snippets_fetcher.h
index 782dd73..03276c8 100644
--- a/components/ntp_snippets/ntp_snippets_fetcher.h
+++ b/components/ntp_snippets/ntp_snippets_fetcher.h
@@ -48,7 +48,7 @@
     base::string16 localized_title;  // Ignored for non-server categories.
     NTPSnippet::PtrVector snippets;
 
-    FetchedCategory(Category c);
+    explicit FetchedCategory(Category c);
     FetchedCategory(FetchedCategory&&);             // = default, in .cc
     ~FetchedCategory();                             // = default, in .cc
     FetchedCategory& operator=(FetchedCategory&&);  // = default, in .cc
diff --git a/components/ntp_snippets/ntp_snippets_service.h b/components/ntp_snippets/ntp_snippets_service.h
index 1a1f0ab..1aac5ec6 100644
--- a/components/ntp_snippets/ntp_snippets_service.h
+++ b/components/ntp_snippets/ntp_snippets_service.h
@@ -5,8 +5,7 @@
 #ifndef COMPONENTS_NTP_SNIPPETS_NTP_SNIPPETS_SERVICE_H_
 #define COMPONENTS_NTP_SNIPPETS_NTP_SNIPPETS_SERVICE_H_
 
-#include <stddef.h>
-
+#include <cstddef>
 #include <map>
 #include <memory>
 #include <set>
@@ -34,16 +33,16 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace image_fetcher {
 class ImageDecoder;
 class ImageFetcher;
-}
+}  // namespace image_fetcher
 
 namespace suggestions {
 class SuggestionsProfile;
-}
+}  // namespace suggestions
 
 namespace ntp_snippets {
 
@@ -51,6 +50,9 @@
 
 // Retrieves fresh content data (articles) from the server, stores them and
 // provides them as content suggestions.
+// This class is final because it does things in its constructor which make it
+// unsafe to derive from it.
+// TODO(treib): Introduce two-phase initialization and make the class not final?
 // TODO(pke): Rename this service to ArticleSuggestionsProvider and move to
 // a subdirectory.
 // TODO(jkrcal): this class grows really, really large. The fact that
@@ -59,8 +61,8 @@
 // ImagerFetcherDeletage ;-)). Instead, the cleaner solution would  be to define
 // a CachedImageFetcher class that handles the caching aspects and looks like an
 // image fetcher to the NTPSnippetService.
-class NTPSnippetsService : public ContentSuggestionsProvider,
-                           public image_fetcher::ImageFetcherDelegate {
+class NTPSnippetsService final : public ContentSuggestionsProvider,
+                                 public image_fetcher::ImageFetcherDelegate {
  public:
   // |application_language_code| should be a ISO 639-1 compliant string, e.g.
   // 'en' or 'en-US'. Note that this code should only specify the language, not
diff --git a/components/ntp_snippets/ntp_snippets_status_service.h b/components/ntp_snippets/ntp_snippets_status_service.h
index d6a9516..bd52c136 100644
--- a/components/ntp_snippets/ntp_snippets_status_service.h
+++ b/components/ntp_snippets/ntp_snippets_status_service.h
@@ -29,7 +29,7 @@
 // relevant changes in their states.
 class NTPSnippetsStatusService : public SigninManagerBase::Observer {
  public:
-  typedef base::Callback<void(DisabledReason)> DisabledReasonChangeCallback;
+  using DisabledReasonChangeCallback = base::Callback<void(DisabledReason)>;
 
   NTPSnippetsStatusService(SigninManagerBase* signin_manager,
                            PrefService* pref_service);
diff --git a/components/ntp_snippets/offline_pages/offline_page_proxy.h b/components/ntp_snippets/offline_pages/offline_page_proxy.h
index 779110a..a4649471 100644
--- a/components/ntp_snippets/offline_pages/offline_page_proxy.h
+++ b/components/ntp_snippets/offline_pages/offline_page_proxy.h
@@ -37,10 +37,11 @@
         const offline_pages::ClientId& client_id) = 0;
 
    protected:
-    virtual ~Observer() {}
+    virtual ~Observer() = default;
   };
 
-  OfflinePageProxy(offline_pages::OfflinePageModel* offline_page_model);
+  explicit OfflinePageProxy(
+      offline_pages::OfflinePageModel* offline_page_model);
 
   // TODO(vitaliii): Remove this function and provide a better way for providers
   // to get data at the start up, while querying OfflinePagesModel only once.
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
index ff3235d..8d489a65 100644
--- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
+++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc
@@ -352,8 +352,7 @@
                                       MakeUniqueID(category, offline_page_id));
 
   std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category);
-  auto it =
-      std::find(dismissed_ids.begin(), dismissed_ids.end(), offline_page_id);
+  auto it = dismissed_ids.find(offline_page_id);
   if (it != dismissed_ids.end()) {
     dismissed_ids.erase(it);
     StoreDismissedIDsToPrefs(category, dismissed_ids);
diff --git a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
index c4552a6..19a058a5 100644
--- a/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
+++ b/components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h
@@ -23,7 +23,7 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace ntp_snippets {
 
diff --git a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
index f79e43b..b2c7431a 100644
--- a/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
+++ b/components/ntp_snippets/physical_web_pages/physical_web_page_suggestions_provider.h
@@ -17,7 +17,7 @@
 
 namespace gfx {
 class Image;
-}
+}  // namespace gfx
 
 namespace ntp_snippets {
 
diff --git a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
index 57cd53eb..bb9540b9 100644
--- a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
+++ b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h
@@ -26,7 +26,7 @@
 // Simple interface to get foreign tab data on demand and on change.
 class ForeignSessionsProvider {
  public:
-  virtual ~ForeignSessionsProvider() {}
+  virtual ~ForeignSessionsProvider() = default;
   virtual bool HasSessionsData() = 0;
   virtual std::vector<const sync_sessions::SyncedSession*>
   GetAllForeignSessions() = 0;
diff --git a/components/test_runner/mock_webrtc_peer_connection_handler.cc b/components/test_runner/mock_webrtc_peer_connection_handler.cc
index 0b623e0d..50c3f5b 100644
--- a/components/test_runner/mock_webrtc_peer_connection_handler.cc
+++ b/components/test_runner/mock_webrtc_peer_connection_handler.cc
@@ -108,6 +108,130 @@
   std::vector<std::pair<std::string, std::string>> values_;
 };
 
+template<typename T>
+WebVector<T> sequenceWithValue(T value) {
+  return WebVector<T>(&value, 1);
+}
+
+class MockWebRTCStatsMember : public blink::WebRTCStatsMember {
+ public:
+  MockWebRTCStatsMember(
+      const std::string& name, blink::WebRTCStatsMemberType type)
+      : name_(name), type_(type) {}
+
+  // blink::WebRTCStatsMember overrides.
+  blink::WebString name() const override {
+    return blink::WebString::fromUTF8(name_);
+  }
+  blink::WebRTCStatsMemberType type() const override {
+    return type_;
+  }
+  bool isDefined() const override { return true; }
+  int32_t valueInt32() const override { return 42; }
+  uint32_t valueUint32() const override { return 42; }
+  int64_t valueInt64() const override { return 42; }
+  uint64_t valueUint64() const override { return 42; }
+  double valueDouble() const override { return 42.0; }
+  blink::WebString valueString() const override {
+    return blink::WebString::fromUTF8("42");
+  }
+  WebVector<int32_t> valueSequenceInt32() const override {
+    return sequenceWithValue<int32_t>(42);
+  }
+  WebVector<uint32_t> valueSequenceUint32() const override {
+    return sequenceWithValue<uint32_t>(42);
+  }
+  WebVector<int64_t> valueSequenceInt64() const override {
+    return sequenceWithValue<int64_t>(42);
+  }
+  WebVector<uint64_t> valueSequenceUint64() const override {
+    return sequenceWithValue<uint64_t>(42);
+  }
+  WebVector<double> valueSequenceDouble() const override {
+    return sequenceWithValue<double>(42.0);
+  }
+  blink::WebVector<blink::WebString> valueSequenceString() const override {
+    return sequenceWithValue<blink::WebString>(
+        blink::WebString::fromUTF8("42"));
+  }
+
+ private:
+  std::string name_;
+  blink::WebRTCStatsMemberType type_;
+};
+
+class MockWebRTCStats : public blink::WebRTCStats {
+ public:
+  MockWebRTCStats(
+      const std::string& id, const std::string& type, double timestamp)
+      : id_(id), type_(type), timestamp_(timestamp) {
+  }
+
+  void addMember(const std::string& name, blink::WebRTCStatsMemberType type) {
+    members_.push_back(std::make_pair(name, type));
+  }
+
+  // blink::WebRTCStats overrides.
+  blink::WebString id() const override {
+    return blink::WebString::fromUTF8(id_);
+  }
+  blink::WebString type() const override {
+    return blink::WebString::fromUTF8(type_);
+  }
+  double timestamp() const override {
+    return timestamp_;
+  }
+  size_t membersCount() const override {
+    return members_.size();
+  }
+  std::unique_ptr<WebRTCStatsMember> getMember(size_t i) const override {
+    return std::unique_ptr<WebRTCStatsMember>(new MockWebRTCStatsMember(
+        members_[i].first, members_[i].second));
+  }
+
+ private:
+  std::string id_;
+  std::string type_;
+  double timestamp_;
+  std::vector<std::pair<std::string, blink::WebRTCStatsMemberType>> members_;
+};
+
+class MockWebRTCStatsReport : public blink::WebRTCStatsReport {
+ public:
+  MockWebRTCStatsReport() : i_(0) {}
+  MockWebRTCStatsReport(const MockWebRTCStatsReport& other)
+      : stats_(other.stats_), i_(0) {}
+
+  void AddStats(const MockWebRTCStats& stats) {
+    stats_.push_back(stats);
+  }
+
+  // blink::WebRTCStatsReport overrides.
+  std::unique_ptr<blink::WebRTCStatsReport> copyHandle() const override {
+    // Because this is just a mock, we copy the underlying stats instead of
+    // referencing the same stats as the original report.
+    return std::unique_ptr<blink::WebRTCStatsReport>(
+        new MockWebRTCStatsReport(*this));
+  }
+  std::unique_ptr<WebRTCStats> getStats(WebString id) const override {
+    for (const MockWebRTCStats& stats : stats_) {
+      if (stats.id() == id)
+        return std::unique_ptr<blink::WebRTCStats>(new MockWebRTCStats(stats));
+    }
+    return nullptr;
+  }
+  std::unique_ptr<blink::WebRTCStats> next() override {
+    if (i_ >= stats_.size())
+      return nullptr;
+    return std::unique_ptr<blink::WebRTCStats>(
+        new MockWebRTCStats(stats_[i_++]));
+  }
+
+ private:
+  std::vector<MockWebRTCStats> stats_;
+  size_t i_;
+};
+
 }  // namespace
 
 MockWebRTCPeerConnectionHandler::MockWebRTCPeerConnectionHandler()
@@ -393,9 +517,23 @@
 
 void MockWebRTCPeerConnectionHandler::getStats(
     std::unique_ptr<blink::WebRTCStatsReportCallback> callback) {
-  // TODO(hbos): When blink::RTCPeerConnection starts using the new |getStats|
-  // this needs to be implemented. crbug.com/627816.
-  NOTREACHED();
+  std::unique_ptr<MockWebRTCStatsReport> report(new MockWebRTCStatsReport());
+  MockWebRTCStats stats("mock-stats-01", "mock-stats", 1234.0);
+  stats.addMember("int32", blink::WebRTCStatsMemberTypeInt32);
+  stats.addMember("uint32", blink::WebRTCStatsMemberTypeUint32);
+  stats.addMember("int64", blink::WebRTCStatsMemberTypeInt64);
+  stats.addMember("uint64", blink::WebRTCStatsMemberTypeUint64);
+  stats.addMember("double", blink::WebRTCStatsMemberTypeDouble);
+  stats.addMember("string", blink::WebRTCStatsMemberTypeString);
+  stats.addMember("sequenceInt32", blink::WebRTCStatsMemberTypeSequenceInt32);
+  stats.addMember("sequenceUint32", blink::WebRTCStatsMemberTypeSequenceUint32);
+  stats.addMember("sequenceInt64", blink::WebRTCStatsMemberTypeSequenceInt64);
+  stats.addMember("sequenceUint64", blink::WebRTCStatsMemberTypeSequenceUint64);
+  stats.addMember("sequenceDouble", blink::WebRTCStatsMemberTypeSequenceDouble);
+  stats.addMember("sequenceString", blink::WebRTCStatsMemberTypeSequenceString);
+  report->AddStats(stats);
+  callback->OnStatsDelivered(std::unique_ptr<blink::WebRTCStatsReport>(
+      report.release()));
 }
 
 void MockWebRTCPeerConnectionHandler::ReportCreationOfDataChannel() {
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 1c6011b..78e6dc9 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1745,7 +1745,6 @@
     switches::kMaxUntiledLayerHeight,
     switches::kMemoryMetrics,
     switches::kMojoLocalStorage,
-    switches::kMojoServiceWorker,
     switches::kMSEAudioBufferSizeLimit,
     switches::kMSEVideoBufferSizeLimit,
     switches::kNoReferrers,
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 17d4cd4..a08d207 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -20,14 +20,11 @@
 #include "content/common/service_worker/embedded_worker_messages.h"
 #include "content/common/service_worker/embedded_worker_settings.h"
 #include "content/common/service_worker/embedded_worker_setup.mojom.h"
-#include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/child_process_host.h"
-#include "content/public/common/content_switches.h"
 #include "ipc/ipc_message.h"
 #include "services/shell/public/cpp/interface_provider.h"
 #include "services/shell/public/cpp/interface_registry.h"
@@ -72,7 +69,7 @@
       worker_process_id, worker_route_id);
 }
 
-void SetupOnUI(
+void RegisterToWorkerDevToolsManagerOnUI(
     int process_id,
     const ServiceWorkerContextCore* service_worker_context,
     const base::WeakPtr<ServiceWorkerContextCore>& service_worker_context_weak,
@@ -80,7 +77,6 @@
     const GURL& url,
     const GURL& scope,
     bool is_installed,
-    mojom::EmbeddedWorkerInstanceClientRequest request,
     const base::Callback<void(int worker_devtools_agent_route_id,
                               bool wait_for_debugger)>& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -96,8 +92,6 @@
                 service_worker_context, service_worker_context_weak,
                 service_worker_version_id, url, scope),
             is_installed);
-    if (request.is_pending())
-      rph->GetRemoteInterfaces()->GetInterface(std::move(request));
   }
   BrowserThread::PostTask(
       BrowserThread::IO,
@@ -217,11 +211,8 @@
  public:
   enum class ProcessAllocationState { NOT_ALLOCATED, ALLOCATING, ALLOCATED };
 
-  StartTask(EmbeddedWorkerInstance* instance,
-            const GURL& script_url,
-            mojom::EmbeddedWorkerInstanceClientRequest request)
+  StartTask(EmbeddedWorkerInstance* instance, const GURL& script_url)
       : instance_(instance),
-        request_(std::move(request)),
         state_(ProcessAllocationState::NOT_ALLOCATED),
         is_installed_(false),
         started_during_browser_startup_(false),
@@ -263,7 +254,7 @@
     // TODO(nhiroki): Reconsider this bizarre layering.
   }
 
-  void Start(std::unique_ptr<EmbeddedWorkerStartParams> params,
+  void Start(std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
              const StatusCallback& callback) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     state_ = ProcessAllocationState::ALLOCATING;
@@ -298,11 +289,12 @@
   bool is_installed() const { return is_installed_; }
 
  private:
-  void OnProcessAllocated(std::unique_ptr<EmbeddedWorkerStartParams> params,
-                          ServiceWorkerStatusCode status,
-                          int process_id,
-                          bool is_new_process,
-                          const EmbeddedWorkerSettings& settings) {
+  void OnProcessAllocated(
+      std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+      ServiceWorkerStatusCode status,
+      int process_id,
+      bool is_new_process,
+      const EmbeddedWorkerSettings& settings) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
     if (status != SERVICE_WORKER_OK) {
@@ -350,22 +342,23 @@
     GURL script_url(params->script_url);
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&SetupOnUI, process_id, instance_->context_.get(),
-                   instance_->context_, service_worker_version_id, script_url,
-                   scope, is_installed_, base::Passed(&request_),
-                   base::Bind(&StartTask::OnSetupOnUICompleted,
+        base::Bind(RegisterToWorkerDevToolsManagerOnUI, process_id,
+                   instance_->context_.get(), instance_->context_,
+                   service_worker_version_id, script_url, scope, is_installed_,
+                   base::Bind(&StartTask::OnRegisteredToDevToolsManager,
                               weak_factory_.GetWeakPtr(), base::Passed(&params),
                               is_new_process)));
   }
 
-  void OnSetupOnUICompleted(std::unique_ptr<EmbeddedWorkerStartParams> params,
-                            bool is_new_process,
-                            int worker_devtools_agent_route_id,
-                            bool wait_for_debugger) {
+  void OnRegisteredToDevToolsManager(
+      std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
+      bool is_new_process,
+      int worker_devtools_agent_route_id,
+      bool wait_for_debugger) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     TRACE_EVENT_ASYNC_STEP_PAST0("ServiceWorker",
                                  "EmbeddedWorkerInstance::Start", this,
-                                 "OnSetupOnUICompleted");
+                                 "OnRegisteredToDevToolsManager");
 
     // Notify the instance that it is registered to the devtools manager.
     instance_->OnRegisteredToDevToolsManager(
@@ -373,14 +366,11 @@
 
     params->worker_devtools_agent_route_id = worker_devtools_agent_route_id;
     params->wait_for_debugger = wait_for_debugger;
-
-    if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled())
-      instance_->SendMojoStartWorker(std::move(params));
-    else
-      SendStartWorker(std::move(params));
+    SendStartWorker(std::move(params));
   }
 
-  void SendStartWorker(std::unique_ptr<EmbeddedWorkerStartParams> params) {
+  void SendStartWorker(
+      std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     ServiceWorkerStatusCode status = instance_->registry_->SendStartWorker(
         std::move(params), instance_->process_id());
@@ -403,10 +393,6 @@
   // |instance_| must outlive |this|.
   EmbeddedWorkerInstance* instance_;
 
-  // Ownership is transferred by base::Passed() to a task after process
-  // allocation.
-  mojom::EmbeddedWorkerInstanceClientRequest request_;
-
   StatusCallback start_callback_;
   ProcessAllocationState state_;
 
@@ -435,7 +421,7 @@
 }
 
 void EmbeddedWorkerInstance::Start(
-    std::unique_ptr<EmbeddedWorkerStartParams> params,
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
     const StatusCallback& callback) {
   if (!context_) {
     callback.Run(SERVICE_WORKER_ERROR_ABORT);
@@ -459,12 +445,7 @@
   params->wait_for_debugger = false;
   params->settings.v8_cache_options = GetV8CacheOptions();
 
-  mojom::EmbeddedWorkerInstanceClientRequest request;
-  if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled())
-    request = mojo::GetProxy(&client_);
-
-  inflight_start_task_.reset(
-      new StartTask(this, params->script_url, std::move(request)));
+  inflight_start_task_.reset(new StartTask(this, params->script_url));
   inflight_start_task_->Start(std::move(params), callback);
 }
 
@@ -585,15 +566,6 @@
   FOR_EACH_OBSERVER(Listener, listener_list_, OnRegisteredToDevToolsManager());
 }
 
-void EmbeddedWorkerInstance::SendMojoStartWorker(
-    std::unique_ptr<EmbeddedWorkerStartParams> params) {
-  client_->StartWorker(*params);
-  registry_->BindWorkerToProcess(process_id(), embedded_worker_id());
-  TRACE_EVENT_ASYNC_STEP_PAST1("ServiceWorker", "EmbeddedWorkerInstance::Start",
-                               this, "SendStartWorker", "Status", "mojo");
-  OnStartWorkerMessageSent();
-}
-
 void EmbeddedWorkerInstance::OnStartWorkerMessageSent() {
   if (!step_time_.is_null()) {
     base::TimeDelta duration = UpdateStepTime();
@@ -688,10 +660,9 @@
   FOR_EACH_OBSERVER(Listener, listener_list_, OnThreadStarted());
 
   shell::mojom::InterfaceProviderPtr exposed_interfaces;
-  interface_registry_->Bind(mojo::GetProxy(&exposed_interfaces));
+  interface_registry_->Bind(GetProxy(&exposed_interfaces));
   shell::mojom::InterfaceProviderPtr remote_interfaces;
-  shell::mojom::InterfaceProviderRequest request =
-      mojo::GetProxy(&remote_interfaces);
+  shell::mojom::InterfaceProviderRequest request = GetProxy(&remote_interfaces);
   remote_interfaces_->Bind(std::move(remote_interfaces));
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 705b773..13aca7e3 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -23,7 +23,6 @@
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/public/common/console_message_level.h"
 #include "url/gurl.h"
@@ -33,6 +32,8 @@
 #undef SendMessage
 #endif
 
+struct EmbeddedWorkerMsg_StartWorker_Params;
+
 namespace IPC {
 class Message;
 }
@@ -45,7 +46,6 @@
 namespace content {
 
 class EmbeddedWorkerRegistry;
-struct EmbeddedWorkerStartParams;
 class MessagePortMessageFilter;
 class ServiceWorkerContextCore;
 
@@ -112,7 +112,7 @@
   // started and evaluated, or when an error occurs.
   // |params| should be populated with service worker version info needed
   // to start the worker.
-  void Start(std::unique_ptr<EmbeddedWorkerStartParams> params,
+  void Start(std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
              const StatusCallback& callback);
 
   // Stops the worker. It is invalid to call this when the worker is
@@ -224,9 +224,6 @@
                                      int worker_devtools_agent_route_id,
                                      bool wait_for_debugger);
 
-  // Sends StartWorker message via Mojo.
-  void SendMojoStartWorker(std::unique_ptr<EmbeddedWorkerStartParams> params);
-
   // Called back from StartTask after a start worker message is sent.
   void OnStartWorkerMessageSent();
 
@@ -309,12 +306,8 @@
   // Current running information.
   std::unique_ptr<EmbeddedWorkerInstance::WorkerProcessHandle> process_handle_;
   int thread_id_;
-
-  // These are connected to the renderer process after OnThreadStarted.
   std::unique_ptr<shell::InterfaceRegistry> interface_registry_;
   std::unique_ptr<shell::InterfaceProvider> remote_interfaces_;
-  // |client_| is used to send messages to the renderer process.
-  mojom::EmbeddedWorkerInstanceClientPtr client_;
 
   // Whether devtools is attached or not.
   bool devtools_attached_;
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 1253a28..f2df6b57 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -8,7 +8,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/stl_util.h"
@@ -17,13 +16,9 @@
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
-#include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/public/common/child_process_host.h"
-#include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -38,15 +33,14 @@
   callback.Run();
 }
 
-std::unique_ptr<EmbeddedWorkerStartParams>
+std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params>
 CreateStartParams(int version_id, const GURL& scope, const GURL& script_url) {
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
-      new EmbeddedWorkerStartParams);
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
+      new EmbeddedWorkerMsg_StartWorker_Params);
   params->service_worker_version_id = version_id;
   params->scope = scope;
   params->script_url = script_url;
   params->pause_after_download = false;
-  params->is_installed = false;
   return params;
 }
 
@@ -103,7 +97,7 @@
                                       const GURL& url) {
     ServiceWorkerStatusCode status;
     base::RunLoop run_loop;
-    std::unique_ptr<EmbeddedWorkerStartParams> params =
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params =
         CreateStartParams(id, pattern, url);
     worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                                 run_loop.QuitClosure()));
@@ -128,24 +122,6 @@
   DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest);
 };
 
-class EmbeddedWorkerInstanceTestP : public EmbeddedWorkerInstanceTest,
-                                    public testing::WithParamInterface<bool> {
- protected:
-  void SetUp() override {
-    is_mojo_enabled_ = GetParam();
-    if (is_mojo_enabled()) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kMojoServiceWorker);
-    }
-    EmbeddedWorkerInstanceTest::SetUp();
-  }
-
-  bool is_mojo_enabled() { return is_mojo_enabled_; }
-
- private:
-  bool is_mojo_enabled_ = false;
-};
-
 // A helper to simulate the start worker sequence is stalled in a worker
 // process.
 class StalledInStartWorkerHelper : public EmbeddedWorkerTestHelper {
@@ -186,11 +162,10 @@
   }
 };
 
-TEST_P(EmbeddedWorkerInstanceTestP, StartAndStop) {
+TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) {
   std::unique_ptr<EmbeddedWorkerInstance> worker =
       embedded_worker_registry()->CreateWorker();
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
-  worker->AddListener(this);
 
   const int64_t service_worker_version_id = 55L;
   const GURL pattern("http://example.com/");
@@ -203,7 +178,7 @@
   // Start should succeed.
   ServiceWorkerStatusCode status;
   base::RunLoop run_loop;
-  std::unique_ptr<EmbeddedWorkerStartParams> params =
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params =
       CreateStartParams(service_worker_version_id, pattern, url);
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               run_loop.QuitClosure()));
@@ -225,21 +200,11 @@
   // EmbeddedWorkerTestHelper.
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
 
-  if (!is_mojo_enabled()) {
-    // Verify that we've sent two messages to start and terminate the worker.
-    ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
-        EmbeddedWorkerMsg_StartWorker::ID));
-  }
-  // StopWorker should be sent in either case.
+  // Verify that we've sent two messages to start and terminate the worker.
+  ASSERT_TRUE(
+      ipc_sink()->GetUniqueMessageMatching(EmbeddedWorkerMsg_StartWorker::ID));
   ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
       EmbeddedWorkerMsg_StopWorker::ID));
-
-  // Check if the IPCs are fired in expected order.
-  ASSERT_EQ(4u, events_.size());
-  EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type);
-  EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type);
-  EXPECT_EQ(STARTED, events_[2].type);
-  EXPECT_EQ(STOPPED, events_[3].type);
 }
 
 // Test that a worker that failed twice will use a new render process
@@ -268,7 +233,7 @@
     // Start once normally.
     ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
     base::RunLoop run_loop;
-    std::unique_ptr<EmbeddedWorkerStartParams> params(
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
         CreateStartParams(service_worker_version_id, pattern, url));
     worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                                 run_loop.QuitClosure()));
@@ -292,7 +257,7 @@
     // Start again.
     ServiceWorkerStatusCode status;
     base::RunLoop run_loop;
-    std::unique_ptr<EmbeddedWorkerStartParams> params(
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
         CreateStartParams(service_worker_version_id, pattern, url));
     worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                                 run_loop.QuitClosure()));
@@ -372,7 +337,7 @@
     // Start worker1.
     ServiceWorkerStatusCode status;
     base::RunLoop run_loop;
-    std::unique_ptr<EmbeddedWorkerStartParams> params(
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
         CreateStartParams(version_id1, pattern, url));
     worker1->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                                  run_loop.QuitClosure()));
@@ -384,7 +349,7 @@
     // Start worker2.
     ServiceWorkerStatusCode status;
     base::RunLoop run_loop;
-    std::unique_ptr<EmbeddedWorkerStartParams> params(
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
         CreateStartParams(version_id2, pattern, url));
     worker2->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                                  run_loop.QuitClosure()));
@@ -421,7 +386,7 @@
 
   // Run the start worker sequence and detach during process allocation.
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, scope, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               base::Bind(&base::DoNothing)));
@@ -453,7 +418,7 @@
 
   // Run the start worker sequence until a start worker message is sent.
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, scope, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               base::Bind(&base::DoNothing)));
@@ -492,7 +457,7 @@
   // Stop the start worker sequence before a process is allocated.
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
 
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, scope, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               base::Bind(&base::DoNothing)));
@@ -542,7 +507,7 @@
   // Run the start worker sequence until pause after download.
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
 
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, scope, url));
   params->pause_after_download = true;
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
@@ -573,7 +538,7 @@
 
   // Run the start worker sequence until a start worker message is sent.
   ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, scope, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               base::Bind(&base::DoNothing)));
@@ -635,7 +600,7 @@
 
   // Start the worker.
   base::RunLoop run_loop;
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, pattern, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               run_loop.QuitClosure()));
@@ -670,7 +635,7 @@
 
   // Attempt to start the worker.
   base::RunLoop run_loop;
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
       CreateStartParams(version_id, pattern, url));
   worker->Start(std::move(params), base::Bind(&SaveStatusAndCall, &status,
                                               run_loop.QuitClosure()));
@@ -684,8 +649,4 @@
   EXPECT_EQ(EmbeddedWorkerStatus::STARTING, events_[1].status);
 }
 
-INSTANTIATE_TEST_CASE_P(EmbeddedWorkerInstanceTest,
-                        EmbeddedWorkerInstanceTestP,
-                        ::testing::Values(false, true));
-
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_registry.cc b/content/browser/service_worker/embedded_worker_registry.cc
index 3859b5aa..1b75f94 100644
--- a/content/browser/service_worker/embedded_worker_registry.cc
+++ b/content/browser/service_worker/embedded_worker_registry.cc
@@ -249,7 +249,7 @@
 }
 
 ServiceWorkerStatusCode EmbeddedWorkerRegistry::SendStartWorker(
-    std::unique_ptr<EmbeddedWorkerStartParams> params,
+    std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
     int process_id) {
   if (!context_)
     return SERVICE_WORKER_ERROR_ABORT;
@@ -269,25 +269,10 @@
   ServiceWorkerStatusCode status =
       Send(process_id, new EmbeddedWorkerMsg_StartWorker(*params));
   if (status == SERVICE_WORKER_OK)
-    BindWorkerToProcess(process_id, embedded_worker_id);
+    worker_process_map_[process_id].insert(embedded_worker_id);
   return status;
 }
 
-void EmbeddedWorkerRegistry::BindWorkerToProcess(int process_id,
-                                                 int embedded_worker_id) {
-  // The ServiceWorkerDispatcherHost is supposed to be created when the process
-  // is created, and keep an entry in process_sender_map_ for its whole
-  // lifetime.
-  DCHECK(base::ContainsKey(process_sender_map_, process_id));
-  DCHECK(GetWorker(embedded_worker_id));
-  DCHECK_EQ(GetWorker(embedded_worker_id)->process_id(), process_id);
-  DCHECK(
-      !base::ContainsKey(worker_process_map_, process_id) ||
-      !base::ContainsKey(worker_process_map_[process_id], embedded_worker_id));
-
-  worker_process_map_[process_id].insert(embedded_worker_id);
-}
-
 ServiceWorkerStatusCode EmbeddedWorkerRegistry::Send(
     int process_id, IPC::Message* message_ptr) {
   std::unique_ptr<IPC::Message> message(message_ptr);
diff --git a/content/browser/service_worker/embedded_worker_registry.h b/content/browser/service_worker/embedded_worker_registry.h
index 70475ef..8e83b1d 100644
--- a/content/browser/service_worker/embedded_worker_registry.h
+++ b/content/browser/service_worker/embedded_worker_registry.h
@@ -18,6 +18,7 @@
 #include "content/common/content_export.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 
+struct EmbeddedWorkerMsg_StartWorker_Params;
 class GURL;
 
 namespace IPC {
@@ -28,7 +29,6 @@
 namespace content {
 
 class EmbeddedWorkerInstance;
-struct EmbeddedWorkerStartParams;
 class MessagePortMessageFilter;
 class ServiceWorkerContextCore;
 
@@ -59,7 +59,7 @@
 
   // Called from EmbeddedWorkerInstance, relayed to the child process.
   ServiceWorkerStatusCode SendStartWorker(
-      std::unique_ptr<EmbeddedWorkerStartParams> params,
+      std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
       int process_id);
   ServiceWorkerStatusCode StopWorker(int process_id,
                                      int embedded_worker_id);
@@ -114,10 +114,10 @@
   FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerInstanceTest,
                            RemoveWorkerInSharedProcess);
 
-  using WorkerInstanceMap = std::map<int, EmbeddedWorkerInstance*>;
-  using ProcessToSenderMap = std::map<int, IPC::Sender*>;
-  using ProcessToMessagePortMessageFilterMap =
-      std::map<int, MessagePortMessageFilter*>;
+  typedef std::map<int, EmbeddedWorkerInstance*> WorkerInstanceMap;
+  typedef std::map<int, IPC::Sender*> ProcessToSenderMap;
+  typedef std::map<int, MessagePortMessageFilter*>
+      ProcessToMessagePortMessageFilterMap;
 
   EmbeddedWorkerRegistry(
       const base::WeakPtr<ServiceWorkerContextCore>& context,
@@ -126,11 +126,6 @@
 
   ServiceWorkerStatusCode Send(int process_id, IPC::Message* message);
 
-  // Called when EmbeddedWorkerInstance is ready for IPC. This function
-  // prepares a route to the child worker thread.
-  // TODO(shimazu): Remove this function once mojofication is completed.
-  void BindWorkerToProcess(int process_id, int embedded_worker_id);
-
   // RemoveWorker is called when EmbeddedWorkerInstance is destructed.
   // |process_id| could be invalid (i.e. ChildProcessHost::kInvalidUniqueID)
   // if it's not running.
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 674827f..daf636d 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -21,7 +21,6 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
 #include "content/common/service_worker/embedded_worker_setup.mojom.h"
-#include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/common/push_event_payload.h"
 #include "content/public/test/mock_render_process_host.h"
@@ -83,66 +82,13 @@
   base::WeakPtr<EmbeddedWorkerTestHelper> helper_;
 };
 
-EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::
-    MockEmbeddedWorkerInstanceClient(
-        base::WeakPtr<EmbeddedWorkerTestHelper> helper)
-    : helper_(helper), binding_(this) {}
-
-EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::
-    ~MockEmbeddedWorkerInstanceClient() {}
-
-void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::StartWorker(
-    const EmbeddedWorkerStartParams& params) {
-  if (!helper_)
-    return;
-
-  embedded_worker_id_ = params.embedded_worker_id;
-
-  EmbeddedWorkerInstance* worker =
-      helper_->registry()->GetWorker(params.embedded_worker_id);
-  ASSERT_TRUE(worker != NULL);
-  EXPECT_EQ(EmbeddedWorkerStatus::STARTING, worker->status());
-
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&EmbeddedWorkerTestHelper::OnStartWorker,
-                 helper_->weak_factory_.GetWeakPtr(), params.embedded_worker_id,
-                 params.service_worker_version_id, params.scope,
-                 params.script_url, params.pause_after_download));
-}
-
-// static
-void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::Bind(
-    const base::WeakPtr<EmbeddedWorkerTestHelper>& helper,
-    mojom::EmbeddedWorkerInstanceClientRequest request) {
-  std::vector<std::unique_ptr<MockEmbeddedWorkerInstanceClient>>* clients =
-      helper->mock_instance_clients();
-  size_t next_client_index = helper->mock_instance_clients_next_index_;
-
-  ASSERT_GE(clients->size(), next_client_index);
-  if (clients->size() == next_client_index) {
-    clients->push_back(
-        base::MakeUnique<MockEmbeddedWorkerInstanceClient>(helper));
-  }
-
-  std::unique_ptr<MockEmbeddedWorkerInstanceClient>& client =
-      clients->at(next_client_index);
-  helper->mock_instance_clients_next_index_ = next_client_index + 1;
-  if (client)
-    client->binding_.Bind(std::move(request));
-}
-
 EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(
     const base::FilePath& user_data_directory)
     : browser_context_(new TestBrowserContext),
       render_process_host_(new MockRenderProcessHost(browser_context_.get())),
-      new_render_process_host_(
-          new MockRenderProcessHost(browser_context_.get())),
       wrapper_(new ServiceWorkerContextWrapper(browser_context_.get())),
-      mock_instance_clients_next_index_(0),
       next_thread_id_(0),
       mock_render_process_id_(render_process_host_->GetID()),
-      new_mock_render_process_id_(new_render_process_host_->GetID()),
       weak_factory_(this) {
   std::unique_ptr<MockServiceWorkerDatabaseTaskManager> database_task_manager(
       new MockServiceWorkerDatabaseTaskManager(
@@ -155,10 +101,16 @@
                                     NewMessagePortMessageFilter());
 
   // Setup process level interface registry.
-  render_process_interface_registry_ =
-      CreateInterfaceRegistry(render_process_host_.get());
-  new_render_process_interface_registry_ =
-      CreateInterfaceRegistry(new_render_process_host_.get());
+  render_process_interface_registry_.reset(new shell::InterfaceRegistry);
+  render_process_interface_registry_->AddInterface(
+      base::Bind(&MockEmbeddedWorkerSetup::Create, weak_factory_.GetWeakPtr()));
+  shell::mojom::InterfaceProviderPtr interfaces;
+  render_process_interface_registry_->Bind(mojo::GetProxy(&interfaces));
+
+  std::unique_ptr<shell::InterfaceProvider> host_remote_interfaces(
+      new shell::InterfaceProvider);
+  host_remote_interfaces->Bind(std::move(interfaces));
+  render_process_host_->SetRemoteInterfaces(std::move(host_remote_interfaces));
 }
 
 EmbeddedWorkerTestHelper::~EmbeddedWorkerTestHelper() {
@@ -375,7 +327,7 @@
 }
 
 void EmbeddedWorkerTestHelper::OnStartWorkerStub(
-    const EmbeddedWorkerStartParams& params) {
+    const EmbeddedWorkerMsg_StartWorker_Params& params) {
   EmbeddedWorkerInstance* worker =
       registry()->GetWorker(params.embedded_worker_id);
   ASSERT_TRUE(worker != NULL);
@@ -492,23 +444,4 @@
   return filter.get();
 }
 
-std::unique_ptr<shell::InterfaceRegistry>
-EmbeddedWorkerTestHelper::CreateInterfaceRegistry(MockRenderProcessHost* rph) {
-  std::unique_ptr<shell::InterfaceRegistry> registry(
-      new shell::InterfaceRegistry);
-  registry->AddInterface(
-      base::Bind(&MockEmbeddedWorkerSetup::Create, weak_factory_.GetWeakPtr()));
-  registry->AddInterface(base::Bind(&MockEmbeddedWorkerInstanceClient::Bind,
-                                    weak_factory_.GetWeakPtr()));
-
-  shell::mojom::InterfaceProviderPtr interfaces;
-  registry->Bind(mojo::GetProxy(&interfaces));
-
-  std::unique_ptr<shell::InterfaceProvider> remote_interfaces(
-      new shell::InterfaceProvider);
-  remote_interfaces->Bind(std::move(interfaces));
-  rph->SetRemoteInterfaces(std::move(remote_interfaces));
-  return registry;
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index 97ec1637..88bf11b 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -15,16 +15,14 @@
 #include "base/containers/hash_tables.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_test_sink.h"
-#include "mojo/public/cpp/bindings/binding.h"
 #include "services/shell/public/interfaces/interface_provider.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
 class GURL;
+struct EmbeddedWorkerMsg_StartWorker_Params;
 struct ServiceWorkerMsg_ExtendableMessageEvent_Params;
 
 namespace shell {
@@ -41,7 +39,6 @@
 class ServiceWorkerContextCore;
 class ServiceWorkerContextWrapper;
 class TestBrowserContext;
-struct EmbeddedWorkerStartParams;
 struct PushEventPayload;
 struct ServiceWorkerFetchRequest;
 
@@ -63,28 +60,6 @@
 class EmbeddedWorkerTestHelper : public IPC::Sender,
                                  public IPC::Listener {
  public:
-  class MockEmbeddedWorkerInstanceClient
-      : public mojom::EmbeddedWorkerInstanceClient {
-   public:
-    explicit MockEmbeddedWorkerInstanceClient(
-        base::WeakPtr<EmbeddedWorkerTestHelper> helper);
-    ~MockEmbeddedWorkerInstanceClient() override;
-
-    static void Bind(const base::WeakPtr<EmbeddedWorkerTestHelper>& helper,
-                     mojom::EmbeddedWorkerInstanceClientRequest request);
-
-   private:
-    // Implementation of mojo interfaces.
-    void StartWorker(const EmbeddedWorkerStartParams& params) override;
-
-    base::WeakPtr<EmbeddedWorkerTestHelper> helper_;
-    mojo::Binding<mojom::EmbeddedWorkerInstanceClient> binding_;
-
-    base::Optional<int> embedded_worker_id_;
-
-    DISALLOW_COPY_AND_ASSIGN(MockEmbeddedWorkerInstanceClient);
-  };
-
   // If |user_data_directory| is empty, the context makes storage stuff in
   // memory.
   explicit EmbeddedWorkerTestHelper(const base::FilePath& user_data_directory);
@@ -105,11 +80,6 @@
   // Inner IPC sink for script context messages sent via EmbeddedWorker.
   IPC::TestSink* inner_ipc_sink() { return &inner_sink_; }
 
-  std::vector<std::unique_ptr<MockEmbeddedWorkerInstanceClient>>*
-  mock_instance_clients() {
-    return &mock_instance_clients_;
-  }
-
   ServiceWorkerContextCore* context();
   ServiceWorkerContextWrapper* context_wrapper() { return wrapper_.get(); }
   void ShutdownContext();
@@ -125,8 +95,9 @@
     return embedded_worker_id_service_worker_version_id_map_;
   }
 
-  // Only used for tests that force creating a new render process.
-  int new_render_process_id() const { return new_mock_render_process_id_; }
+  // Only used for tests that force creating a new render process. There is no
+  // corresponding MockRenderProcessHost.
+  int new_render_process_id() const { return mock_render_process_id_ + 1; }
 
   TestBrowserContext* browser_context() { return browser_context_.get(); }
 
@@ -187,7 +158,7 @@
 
   class MockEmbeddedWorkerSetup;
 
-  void OnStartWorkerStub(const EmbeddedWorkerStartParams& params);
+  void OnStartWorkerStub(const EmbeddedWorkerMsg_StartWorker_Params& params);
   void OnResumeAfterDownloadStub(int embedded_worker_id);
   void OnStopWorkerStub(int embedded_worker_id);
   void OnMessageToWorkerStub(int thread_id,
@@ -208,29 +179,18 @@
 
   MessagePortMessageFilter* NewMessagePortMessageFilter();
 
-  std::unique_ptr<shell::InterfaceRegistry> CreateInterfaceRegistry(
-      MockRenderProcessHost* rph);
-
   std::unique_ptr<TestBrowserContext> browser_context_;
   std::unique_ptr<MockRenderProcessHost> render_process_host_;
-  std::unique_ptr<MockRenderProcessHost> new_render_process_host_;
 
   scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
 
   IPC::TestSink sink_;
   IPC::TestSink inner_sink_;
 
-  std::vector<std::unique_ptr<MockEmbeddedWorkerInstanceClient>>
-      mock_instance_clients_;
-  size_t mock_instance_clients_next_index_;
-
   int next_thread_id_;
   int mock_render_process_id_;
-  int new_mock_render_process_id_;
 
   std::unique_ptr<shell::InterfaceRegistry> render_process_interface_registry_;
-  std::unique_ptr<shell::InterfaceRegistry>
-      new_render_process_interface_registry_;
 
   std::map<int, int64_t> embedded_worker_id_service_worker_version_id_map_;
 
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 63a1c47..fc1c655 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -337,20 +337,10 @@
 
 }  // namespace
 
-class ServiceWorkerBrowserTest : public testing::WithParamInterface<bool>,
-                                 public ContentBrowserTest {
+class ServiceWorkerBrowserTest : public ContentBrowserTest {
  protected:
   using self = ServiceWorkerBrowserTest;
 
-  void SetUp() override {
-    is_mojo_enabled_ = GetParam();
-    if (is_mojo_enabled()) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kMojoServiceWorker);
-    }
-    ContentBrowserTest::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
     StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
@@ -378,7 +368,6 @@
 
   ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
   ServiceWorkerContext* public_context() { return wrapper(); }
-  bool is_mojo_enabled() const { return is_mojo_enabled_; }
 
   void AssociateRendererProcessToPattern(const GURL& pattern) {
     wrapper_->process_manager()->AddProcessReferenceToPattern(
@@ -387,7 +376,6 @@
 
  private:
   scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
-  bool is_mojo_enabled_ = false;
 };
 
 class ConsoleListener : public EmbeddedWorkerInstance::Listener {
@@ -799,7 +787,7 @@
   std::unique_ptr<ServiceWorkerFetchDispatcher> fetch_dispatcher_;
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, StartAndStop) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
                            "/service_worker/worker.js"));
@@ -825,7 +813,7 @@
   ASSERT_EQ(SERVICE_WORKER_OK, status);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, StartNotFound) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
                            "/service_worker/nonexistent.js"));
@@ -834,7 +822,7 @@
   StartWorker(SERVICE_WORKER_ERROR_NETWORK);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, ReadResourceFailure) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, ReadResourceFailure) {
   // Create a registration.
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
@@ -865,7 +853,7 @@
                         SERVICE_WORKER_ERROR_NOT_FOUND);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        ReadResourceFailure_WaitingWorker) {
   // Create a registration and active version.
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
@@ -914,24 +902,24 @@
                         SERVICE_WORKER_OK);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, Install) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) {
   InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_Fulfilled) {
   InstallTestHelper("/service_worker/worker_install_fulfilled.js",
                     SERVICE_WORKER_OK);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        InstallWithFetchHandler) {
   InstallTestHelper("/service_worker/fetch_event.js", SERVICE_WORKER_OK);
   EXPECT_EQ(ServiceWorkerVersion::FetchHandlerExistence::EXISTS,
             version_->fetch_handler_existence());
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        InstallWithoutFetchHandler) {
   InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
   EXPECT_EQ(ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST,
@@ -940,31 +928,31 @@
 
 // Check that ServiceWorker script requests set a "Service-Worker: script"
 // header.
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        ServiceWorkerScriptHeader) {
   embedded_test_server()->RegisterRequestHandler(
       base::Bind(&VerifyServiceWorkerHeaderInRequest));
   InstallTestHelper("/service_worker/generated_sw.js", SERVICE_WORKER_OK);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        Activate_NoEventListener) {
   ActivateTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
   ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, version_->status());
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
   ActivateTestHelper("/service_worker/worker_activate_rejected.js",
                      SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_Rejected) {
   InstallTestHelper("/service_worker/worker_install_rejected.js",
                     SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        InstallWithWaitUntil_RejectConsoleMessage) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
@@ -1009,7 +997,7 @@
   base::Closure quit_;
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutStartingWorker) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
                            "/service_worker/while_true_worker.js"));
@@ -1046,7 +1034,7 @@
   EXPECT_EQ(SERVICE_WORKER_ERROR_TIMEOUT, status);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, TimeoutWorkerInEvent) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, TimeoutWorkerInEvent) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread,
                            base::Unretained(this),
                            "/service_worker/while_true_in_install_worker.js"));
@@ -1081,7 +1069,7 @@
   EXPECT_EQ(SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED, status);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
   ServiceWorkerFetchEventResult result;
   ServiceWorkerResponse response;
   std::unique_ptr<storage::BlobDataHandle> blob_data_handle;
@@ -1103,7 +1091,7 @@
   EXPECT_EQ("This resource is gone. Gone, gone, gone.", body);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        FetchEvent_ResponseViaCache) {
   ServiceWorkerFetchEventResult result;
   ServiceWorkerResponse response1;
@@ -1130,7 +1118,7 @@
   EXPECT_EQ("cache_name", response2.cache_storage_cache_name);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        FetchEvent_respondWithRejection) {
   ServiceWorkerFetchEventResult result;
   ServiceWorkerResponse response;
@@ -1185,7 +1173,7 @@
   bool data_saver_enabled_;
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchWithSaveData) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchWithSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::Bind(&VerifySaveDataHeaderInRequest));
   MockContentBrowserClient content_browser_client;
@@ -1196,7 +1184,7 @@
   SetBrowserClientForTesting(old_client);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
                        RequestWorkerScriptWithSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::Bind(&VerifySaveDataHeaderInRequest));
@@ -1208,7 +1196,7 @@
   SetBrowserClientForTesting(old_client);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserTest, FetchWithoutSaveData) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchWithoutSaveData) {
   embedded_test_server()->RegisterRequestHandler(
       base::Bind(&VerifySaveDataHeaderNotInRequest));
   MockContentBrowserClient content_browser_client;
@@ -1218,7 +1206,7 @@
   SetBrowserClientForTesting(old_client);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, FetchPageWithSaveData) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, FetchPageWithSaveData) {
   const char kPageUrl[] = "/service_worker/handle_fetch.html";
   const char kWorkerUrl[] = "/service_worker/add_save_data_to_title.js";
   MockContentBrowserClient content_browser_client;
@@ -1253,7 +1241,7 @@
 // Tests that when data saver is enabled and a cross-origin fetch by a webpage
 // is intercepted by a serviceworker, and the serviceworker does a fetch, the
 // preflight request does not have save-data in Access-Control-Request-Headers.
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, CrossOriginFetchWithSaveData) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, CrossOriginFetchWithSaveData) {
   const char kPageUrl[] = "/service_worker/fetch_cross_origin.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_pass_through.js";
   net::EmbeddedTestServer cross_origin_server;
@@ -1296,7 +1284,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
                        FetchPageWithSaveDataPassThroughOnFetch) {
   const char kPageUrl[] = "/service_worker/pass_through_fetch.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_pass_through.js";
@@ -1330,7 +1318,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, Reload) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, Reload) {
   const char kPageUrl[] = "/service_worker/reload.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_reload.js";
   scoped_refptr<WorkerActivatedObserver> observer =
@@ -1367,7 +1355,7 @@
 #else
 #define MAYBE_ResponseFromHTTPSServiceWorkerIsMarkedAsSecure ResponseFromHTTPSServiceWorkerIsMarkedAsSecure
 #endif
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
                        MAYBE_ResponseFromHTTPSServiceWorkerIsMarkedAsSecure) {
   const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_blob.js";
@@ -1403,7 +1391,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest,
                        ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure) {
   const char kPageUrl[] = "/service_worker/fetch_event_blob.html";
   const char kWorkerUrl[] = "/service_worker/fetch_event_blob.js";
@@ -1435,7 +1423,7 @@
   run_loop.Run();
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, ImportsBustMemcache) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) {
   const char kScopeUrl[] = "/service_worker/imports_bust_memcache_scope/";
   const char kPageUrl[] = "/service_worker/imports_bust_memcache.html";
   const char kScriptUrl[] = "/service_worker/worker_with_one_import.js";
@@ -1500,7 +1488,7 @@
   return result;
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBlackBoxBrowserTest, Registration) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) {
   // Close the only window to be sure we're not re-using its RenderProcessHost.
   shell()->Close();
   EXPECT_EQ(0, CountRenderProcessHosts());
@@ -1582,7 +1570,7 @@
 #else
 #define MAYBE_CrossSiteTransfer CrossSiteTransfer
 #endif
-IN_PROC_BROWSER_TEST_P(ServiceWorkerBrowserTest, MAYBE_CrossSiteTransfer) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, MAYBE_CrossSiteTransfer) {
   // The first page registers a service worker.
   const char kRegisterPageUrl[] = "/service_worker/cross_site_xfer.html";
   const base::string16 kOKTitle1(base::ASCIIToUTF16("OK_1"));
@@ -1636,7 +1624,7 @@
   base::Closure cache_updated_closure_;
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerVersionBrowserV8CacheTest, Restart) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserV8CacheTest, Restart) {
   RunOnIOThread(base::Bind(&self::SetUpRegistrationAndListenerOnIOThread,
                            base::Unretained(this),
                            "/service_worker/worker.js"));
@@ -1903,7 +1891,7 @@
 const int ServiceWorkerV8CacheStrategiesTest::kV8CacheTimeStampDataSize =
     sizeof(unsigned) + sizeof(double);
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CacheStrategiesTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CacheStrategiesTest,
                        V8CacheOnCacheStorage) {
   // The strategy is "aggressive" on default.
   CheckStrategyIsAggressive();
@@ -1923,7 +1911,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerV8CacheStrategiesNoneTest);
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CacheStrategiesNoneTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CacheStrategiesNoneTest,
                        V8CacheOnCacheStorage) {
   CheckStrategyIsNone();
 }
@@ -1942,7 +1930,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerV8CacheStrategiesNormalTest);
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CacheStrategiesNormalTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CacheStrategiesNormalTest,
                        V8CacheOnCacheStorage) {
   CheckStrategyIsNormal();
 }
@@ -1961,7 +1949,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerV8CacheStrategiesAggressiveTest);
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerV8CacheStrategiesAggressiveTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerV8CacheStrategiesAggressiveTest,
                        V8CacheOnCacheStorage) {
   CheckStrategyIsAggressive();
 }
@@ -2012,7 +2000,7 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDisableWebSecurityTest);
 };
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest,
+IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest,
                        GetRegistrationNoCrash) {
   const char kPageUrl[] =
       "/service_worker/disable_web_security_get_registration.html";
@@ -2020,13 +2008,13 @@
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, RegisterNoCrash) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, RegisterNoCrash) {
   const char kPageUrl[] = "/service_worker/disable_web_security_register.html";
   const char kScopeUrl[] = "/service_worker/";
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, UnregisterNoCrash) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, UnregisterNoCrash) {
   const char kPageUrl[] =
       "/service_worker/disable_web_security_unregister.html";
   const char kScopeUrl[] = "/service_worker/scope/";
@@ -2035,7 +2023,7 @@
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-IN_PROC_BROWSER_TEST_P(ServiceWorkerDisableWebSecurityTest, UpdateNoCrash) {
+IN_PROC_BROWSER_TEST_F(ServiceWorkerDisableWebSecurityTest, UpdateNoCrash) {
   const char kPageUrl[] = "/service_worker/disable_web_security_update.html";
   const char kScopeUrl[] = "/service_worker/scope/";
   const char kWorkerUrl[] = "/service_worker/fetch_event_blob.js";
@@ -2043,32 +2031,4 @@
   RunTestWithCrossOriginURL(kPageUrl, kScopeUrl);
 }
 
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerBrowserTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerVersionBrowserV8CacheTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerVersionBrowserTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerBlackBoxBrowserTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerV8CacheStrategiesTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerV8CacheStrategiesNoneTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerV8CacheStrategiesNormalTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerV8CacheStrategiesAggressiveTest,
-                        ::testing::Values(true, false));
-INSTANTIATE_TEST_CASE_P(ServiceWorkerBrowserTest,
-                        ServiceWorkerDisableWebSecurityTest,
-                        ::testing::Values(true, false));
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 50ed74f0..7762d42 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -1620,7 +1620,7 @@
     ASSERT_TRUE(start_msg);
     EmbeddedWorkerMsg_StartWorker::Param param;
     EmbeddedWorkerMsg_StartWorker::Read(start_msg, &param);
-    const EmbeddedWorkerStartParams& start_params = std::get<0>(param);
+    EmbeddedWorkerMsg_StartWorker_Params start_params = std::get<0>(param);
     EXPECT_FALSE(start_params.pause_after_download);
     sink->ClearMessages();
   }
@@ -1635,7 +1635,7 @@
     ASSERT_TRUE(start_msg);
     EmbeddedWorkerMsg_StartWorker::Param param;
     EmbeddedWorkerMsg_StartWorker::Read(start_msg, &param);
-    const EmbeddedWorkerStartParams& start_params = std::get<0>(param);
+    EmbeddedWorkerMsg_StartWorker_Params start_params = std::get<0>(param);
     EXPECT_TRUE(start_params.pause_after_download);
     sink->ClearMessages();
   }
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 337e676..7c33cb28 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -35,7 +35,6 @@
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/common/service_worker/embedded_worker_messages.h"
-#include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_type_converters.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -1407,8 +1406,8 @@
 
   StartTimeoutTimer();
 
-  std::unique_ptr<EmbeddedWorkerStartParams> params(
-      new EmbeddedWorkerStartParams());
+  std::unique_ptr<EmbeddedWorkerMsg_StartWorker_Params> params(
+      new EmbeddedWorkerMsg_StartWorker_Params());
   params->service_worker_version_id = version_id_;
   params->scope = scope_;
   params->script_url = script_url_;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 63e8ba5..df6d152 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -306,8 +306,6 @@
     "send_zygote_child_ping_linux.cc",
     "service_worker/embedded_worker_messages.h",
     "service_worker/embedded_worker_settings.h",
-    "service_worker/embedded_worker_start_params.cc",
-    "service_worker/embedded_worker_start_params.h",
     "service_worker/service_worker_client_info.cc",
     "service_worker/service_worker_client_info.h",
     "service_worker/service_worker_messages.h",
@@ -566,7 +564,6 @@
     "render_frame_message_filter.mojom",
     "render_message_filter.mojom",
     "render_widget_window_tree_client_factory.mojom",
-    "service_worker/embedded_worker.mojom",
     "service_worker/embedded_worker_setup.mojom",
     "storage_partition_service.mojom",
     "url_loader.mojom",
diff --git a/content/common/service_worker/embedded_worker.mojom b/content/common/service_worker/embedded_worker.mojom
deleted file mode 100644
index d15d62b..0000000
--- a/content/common/service_worker/embedded_worker.mojom
+++ /dev/null
@@ -1,16 +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.
-
-module content.mojom;
-
-import "services/shell/public/interfaces/interface_provider.mojom";
-import "url/mojo/url.mojom";
-
-[Native]
-struct EmbeddedWorkerStartParams;
-
-// Interface to control a renderer-side worker's environment.
-interface EmbeddedWorkerInstanceClient {
-  StartWorker(EmbeddedWorkerStartParams params);
-};
diff --git a/content/common/service_worker/embedded_worker.typemap b/content/common/service_worker/embedded_worker.typemap
deleted file mode 100644
index 664936b..0000000
--- a/content/common/service_worker/embedded_worker.typemap
+++ /dev/null
@@ -1,10 +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.
-
-mojom = "//content/common/service_worker/embedded_worker.mojom"
-public_headers =
-    [ "//content/common/service_worker/embedded_worker_start_params.h" ]
-traits_headers =
-    [ "//content/common/service_worker/embedded_worker_messages.h" ]
-type_mappings = [ "content.mojom.EmbeddedWorkerStartParams=::content::EmbeddedWorkerStartParams" ]
diff --git a/content/common/service_worker/embedded_worker_messages.h b/content/common/service_worker/embedded_worker_messages.h
index 34dc1eb..a245c4e0 100644
--- a/content/common/service_worker/embedded_worker_messages.h
+++ b/content/common/service_worker/embedded_worker_messages.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "content/common/service_worker/embedded_worker_settings.h"
-#include "content/common/service_worker/embedded_worker_start_params.h"
 #include "content/public/common/console_message_level.h"
 #include "content/public/common/web_preferences.h"
 #include "ipc/ipc_message.h"
@@ -28,17 +27,17 @@
 IPC_STRUCT_TRAITS_END()
 
 // Parameters structure for EmbeddedWorkerMsg_StartWorker.
-IPC_STRUCT_TRAITS_BEGIN(content::EmbeddedWorkerStartParams)
-  IPC_STRUCT_TRAITS_MEMBER(embedded_worker_id)
-  IPC_STRUCT_TRAITS_MEMBER(service_worker_version_id)
-  IPC_STRUCT_TRAITS_MEMBER(scope)
-  IPC_STRUCT_TRAITS_MEMBER(script_url)
-  IPC_STRUCT_TRAITS_MEMBER(worker_devtools_agent_route_id)
-  IPC_STRUCT_TRAITS_MEMBER(pause_after_download)
-  IPC_STRUCT_TRAITS_MEMBER(wait_for_debugger)
-  IPC_STRUCT_TRAITS_MEMBER(is_installed)
-  IPC_STRUCT_TRAITS_MEMBER(settings)
-IPC_STRUCT_TRAITS_END()
+IPC_STRUCT_BEGIN(EmbeddedWorkerMsg_StartWorker_Params)
+  IPC_STRUCT_MEMBER(int, embedded_worker_id)
+  IPC_STRUCT_MEMBER(int64_t, service_worker_version_id)
+  IPC_STRUCT_MEMBER(GURL, scope)
+  IPC_STRUCT_MEMBER(GURL, script_url)
+  IPC_STRUCT_MEMBER(int, worker_devtools_agent_route_id)
+  IPC_STRUCT_MEMBER(bool, pause_after_download)
+  IPC_STRUCT_MEMBER(bool, wait_for_debugger)
+  IPC_STRUCT_MEMBER(bool, is_installed)
+  IPC_STRUCT_MEMBER(content::EmbeddedWorkerSettings, settings)
+IPC_STRUCT_END()
 
 // Parameters structure for EmbeddedWorkerHostMsg_ReportConsoleMessage.
 // The data members directly correspond to parameters of
@@ -53,7 +52,7 @@
 
 // Browser -> Renderer message to create a new embedded worker context.
 IPC_MESSAGE_CONTROL1(EmbeddedWorkerMsg_StartWorker,
-                     content::EmbeddedWorkerStartParams /* params */)
+                     EmbeddedWorkerMsg_StartWorker_Params /* params */)
 
 // Browser -> Renderer message to resume a worker that has been started
 // with the pause_after_download option.
diff --git a/content/common/service_worker/embedded_worker_start_params.cc b/content/common/service_worker/embedded_worker_start_params.cc
deleted file mode 100644
index 93f62ff..0000000
--- a/content/common/service_worker/embedded_worker_start_params.cc
+++ /dev/null
@@ -1,11 +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.
-
-#include "content/common/service_worker/embedded_worker_start_params.h"
-
-namespace content {
-
-EmbeddedWorkerStartParams::EmbeddedWorkerStartParams() {}
-
-}  // namespace content
diff --git a/content/common/service_worker/embedded_worker_start_params.h b/content/common/service_worker/embedded_worker_start_params.h
deleted file mode 100644
index 9e2ef2a..0000000
--- a/content/common/service_worker/embedded_worker_start_params.h
+++ /dev/null
@@ -1,30 +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.
-
-#include "content/common/content_export.h"
-#include "content/common/service_worker/embedded_worker_settings.h"
-#include "url/gurl.h"
-
-#ifndef CONTENT_COMMON_SERVICE_WORKER_EMBEDDED_WORKER_START_PARAMS_H_
-#define CONTENT_COMMON_SERVICE_WORKER_EMBEDDED_WORKER_START_PARAMS_H_
-
-namespace content {
-
-struct CONTENT_EXPORT EmbeddedWorkerStartParams {
-  EmbeddedWorkerStartParams();
-
-  int embedded_worker_id;
-  int64_t service_worker_version_id;
-  GURL scope;
-  GURL script_url;
-  int worker_devtools_agent_route_id;
-  bool pause_after_download;
-  bool wait_for_debugger;
-  bool is_installed;
-  EmbeddedWorkerSettings settings;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_SERVICE_WORKER_EMBEDDED_WORKER_START_PARAMS_H_
diff --git a/content/common/service_worker/service_worker_utils.cc b/content/common/service_worker/service_worker_utils.cc
index fb3e7251..30faed4 100644
--- a/content/common/service_worker/service_worker_utils.cc
+++ b/content/common/service_worker/service_worker_utils.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "content/public/common/origin_util.h"
@@ -119,12 +118,6 @@
          OriginCanAccessServiceWorkers(script_url);
 }
 
-// static
-bool ServiceWorkerUtils::IsMojoForServiceWorkerEnabled() {
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kMojoServiceWorker);
-}
-
 bool LongestScopeMatcher::MatchLongest(const GURL& scope) {
   if (!ServiceWorkerUtils::ScopeMatches(scope, url_))
     return false;
diff --git a/content/common/service_worker/service_worker_utils.h b/content/common/service_worker/service_worker_utils.h
index b0c3cc8de..4ea403a3 100644
--- a/content/common/service_worker/service_worker_utils.h
+++ b/content/common/service_worker/service_worker_utils.h
@@ -46,8 +46,6 @@
                                        const GURL& pattern,
                                        const GURL& script_url);
 
-  static bool IsMojoForServiceWorkerEnabled();
-
   // Returns true when '--disable-web-security' flag is set. Otherwise returns
   // whether the all origins of |urls| are same as the origin of |url|.
   template <typename... Args>
diff --git a/content/common/typemaps.gni b/content/common/typemaps.gni
index 1b08a89..2dd3ed67 100644
--- a/content/common/typemaps.gni
+++ b/content/common/typemaps.gni
@@ -6,5 +6,4 @@
   "//content/common/url_loader_status.typemap",
   "//content/common/url_request.typemap",
   "//content/common/url_response_head.typemap",
-  "//content/common/service_worker/embedded_worker.typemap",
 ]
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json
index bfb147b..399f7dc 100644
--- a/content/public/app/mojo/content_renderer_manifest.json
+++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -6,7 +6,6 @@
     "provided": {
       "all_interfaces": [ "*" ],
       "browser": [
-        "content::mojom::EmbeddedWorkerInstanceClient",
         "content::mojom::EmbeddedWorkerSetup",
         "content::mojom::FrameFactory",
         "content::mojom::TestMojoService",
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 2db58be..223d56d 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -653,9 +653,6 @@
 // Use a Mojo-based LocalStorage implementation.
 const char kMojoLocalStorage[]              = "mojo-local-storage";
 
-// Use a Mojo-based ServiceWorker implementation.
-const char kMojoServiceWorker[]             = "mojo-service-worker";
-
 // Mutes audio sent to the audio device so it is not audible during
 // automated testing.
 const char kMuteAudio[]                     = "mute-audio";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 3a36973a..4d065ca 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -197,7 +197,6 @@
 CONTENT_EXPORT extern const char kMHTMLSkipNostoreMain[];
 CONTENT_EXPORT extern const char kMHTMLSkipNostoreAll[];
 CONTENT_EXPORT extern const char kMojoLocalStorage[];
-CONTENT_EXPORT extern const char kMojoServiceWorker[];
 CONTENT_EXPORT extern const char kMuteAudio[];
 CONTENT_EXPORT extern const char kNoReferrers[];
 CONTENT_EXPORT extern const char kNoSandbox[];
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index cb51761..8df6b73 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -345,8 +345,6 @@
     "service_worker/embedded_worker_devtools_agent.h",
     "service_worker/embedded_worker_dispatcher.cc",
     "service_worker/embedded_worker_dispatcher.h",
-    "service_worker/embedded_worker_instance_client_impl.cc",
-    "service_worker/embedded_worker_instance_client_impl.h",
     "service_worker/service_worker_context_client.cc",
     "service_worker/service_worker_context_client.h",
     "service_worker/service_worker_context_message_filter.cc",
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 96b146c..8160520 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -119,7 +119,6 @@
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/scheduler/resource_dispatch_throttler.h"
 #include "content/renderer/service_worker/embedded_worker_dispatcher.h"
-#include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
 #include "content/renderer/service_worker/service_worker_context_client.h"
 #include "content/renderer/service_worker/service_worker_context_message_filter.h"
 #include "content/renderer/shared_worker/embedded_shared_worker_stub.h"
@@ -849,11 +848,8 @@
   GetContentClient()->renderer()->ExposeInterfacesToBrowser(
       GetInterfaceRegistry());
 
-  GetInterfaceRegistry()->AddInterface(base::Bind(&CreateFrameFactory));
-  GetInterfaceRegistry()->AddInterface(base::Bind(&CreateEmbeddedWorkerSetup));
-  GetInterfaceRegistry()->AddInterface(
-      base::Bind(&EmbeddedWorkerInstanceClientImpl::Create,
-                 base::Unretained(embedded_worker_dispatcher_.get())));
+  GetInterfaceRegistry()->AddInterface(base::Bind(CreateFrameFactory));
+  GetInterfaceRegistry()->AddInterface(base::Bind(CreateEmbeddedWorkerSetup));
 
   GetRemoteInterfaces()->GetInterface(
       mojo::GetProxy(&storage_partition_service_));
diff --git a/content/renderer/service_worker/embedded_worker_dispatcher.cc b/content/renderer/service_worker/embedded_worker_dispatcher.cc
index bd49677..ec12c06d 100644
--- a/content/renderer/service_worker/embedded_worker_dispatcher.cc
+++ b/content/renderer/service_worker/embedded_worker_dispatcher.cc
@@ -10,6 +10,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/child/child_process.h"
+#include "content/child/scoped_child_process_reference.h"
 #include "content/child/thread_safe_sender.h"
 #include "content/child/worker_thread_registry.h"
 #include "content/common/devtools_messages.h"
@@ -26,14 +27,23 @@
 
 namespace content {
 
-EmbeddedWorkerDispatcher::WorkerWrapper::WorkerWrapper(
-    blink::WebEmbeddedWorker* worker,
-    int devtools_agent_route_id)
-    : worker_(worker),
-      dev_tools_agent_(
-          new EmbeddedWorkerDevToolsAgent(worker, devtools_agent_route_id)) {}
+// A thin wrapper of WebEmbeddedWorker which also adds and releases process
+// references automatically.
+class EmbeddedWorkerDispatcher::WorkerWrapper {
+ public:
+  WorkerWrapper(blink::WebEmbeddedWorker* worker, int devtools_agent_route_id)
+      : worker_(worker),
+        dev_tools_agent_(
+            new EmbeddedWorkerDevToolsAgent(worker, devtools_agent_route_id)) {}
+  ~WorkerWrapper() {}
 
-EmbeddedWorkerDispatcher::WorkerWrapper::~WorkerWrapper() {}
+  blink::WebEmbeddedWorker* worker() { return worker_.get(); }
+
+ private:
+  ScopedChildProcessReference process_ref_;
+  std::unique_ptr<blink::WebEmbeddedWorker> worker_;
+  std::unique_ptr<EmbeddedWorkerDevToolsAgent> dev_tools_agent_;
+};
 
 EmbeddedWorkerDispatcher::EmbeddedWorkerDispatcher() : weak_factory_(this) {}
 
@@ -69,15 +79,35 @@
 }
 
 void EmbeddedWorkerDispatcher::OnStartWorker(
-    const EmbeddedWorkerStartParams& params) {
+    const EmbeddedWorkerMsg_StartWorker_Params& params) {
   DCHECK(!workers_.Lookup(params.embedded_worker_id));
   TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerDispatcher::OnStartWorker");
-  std::unique_ptr<WorkerWrapper> wrapper = StartWorkerContext(
-      params, base::MakeUnique<ServiceWorkerContextClient>(
-                  params.embedded_worker_id, params.service_worker_version_id,
-                  params.scope, params.script_url,
-                  params.worker_devtools_agent_route_id));
-  RegisterWorker(params.embedded_worker_id, std::move(wrapper));
+  std::unique_ptr<WorkerWrapper> wrapper(new WorkerWrapper(
+      blink::WebEmbeddedWorker::create(
+          new ServiceWorkerContextClient(params.embedded_worker_id,
+                                         params.service_worker_version_id,
+                                         params.scope, params.script_url,
+                                         params.worker_devtools_agent_route_id),
+          NULL),
+      params.worker_devtools_agent_route_id));
+
+  blink::WebEmbeddedWorkerStartData start_data;
+  start_data.scriptURL = params.script_url;
+  start_data.userAgent = base::UTF8ToUTF16(GetContentClient()->GetUserAgent());
+  start_data.waitForDebuggerMode =
+      params.wait_for_debugger ?
+          blink::WebEmbeddedWorkerStartData::WaitForDebugger :
+          blink::WebEmbeddedWorkerStartData::DontWaitForDebugger;
+  start_data.v8CacheOptions = static_cast<blink::WebSettings::V8CacheOptions>(
+      params.settings.v8_cache_options);
+  start_data.dataSaverEnabled = params.settings.data_saver_enabled;
+  start_data.pauseAfterDownloadMode =
+      params.pause_after_download
+          ? blink::WebEmbeddedWorkerStartData::PauseAfterDownload
+          : blink::WebEmbeddedWorkerStartData::DontPauseAfterDownload;
+
+  wrapper->worker()->startWorkerContext(start_data);
+  workers_.AddWithID(wrapper.release(), params.embedded_worker_id);
 }
 
 void EmbeddedWorkerDispatcher::OnStopWorker(int embedded_worker_id) {
@@ -133,37 +163,4 @@
       target_level, blink::WebString::fromUTF8(message)));
 }
 
-std::unique_ptr<EmbeddedWorkerDispatcher::WorkerWrapper>
-EmbeddedWorkerDispatcher::StartWorkerContext(
-    const EmbeddedWorkerStartParams& params,
-    std::unique_ptr<ServiceWorkerContextClient> context_client) {
-  std::unique_ptr<WorkerWrapper> wrapper(new WorkerWrapper(
-      blink::WebEmbeddedWorker::create(context_client.release(), nullptr),
-      params.worker_devtools_agent_route_id));
-
-  blink::WebEmbeddedWorkerStartData start_data;
-  start_data.scriptURL = params.script_url;
-  start_data.userAgent = base::UTF8ToUTF16(GetContentClient()->GetUserAgent());
-  start_data.waitForDebuggerMode =
-      params.wait_for_debugger
-          ? blink::WebEmbeddedWorkerStartData::WaitForDebugger
-          : blink::WebEmbeddedWorkerStartData::DontWaitForDebugger;
-  start_data.v8CacheOptions = static_cast<blink::WebSettings::V8CacheOptions>(
-      params.settings.v8_cache_options);
-  start_data.dataSaverEnabled = params.settings.data_saver_enabled;
-  start_data.pauseAfterDownloadMode =
-      params.pause_after_download
-          ? blink::WebEmbeddedWorkerStartData::PauseAfterDownload
-          : blink::WebEmbeddedWorkerStartData::DontPauseAfterDownload;
-
-  wrapper->worker()->startWorkerContext(start_data);
-  return wrapper;
-}
-
-void EmbeddedWorkerDispatcher::RegisterWorker(
-    int embedded_worker_id,
-    std::unique_ptr<WorkerWrapper> wrapper) {
-  workers_.AddWithID(wrapper.release(), embedded_worker_id);
-}
-
 }  // namespace content
diff --git a/content/renderer/service_worker/embedded_worker_dispatcher.h b/content/renderer/service_worker/embedded_worker_dispatcher.h
index 9c706fa8..e77086f7 100644
--- a/content/renderer/service_worker/embedded_worker_dispatcher.h
+++ b/content/renderer/service_worker/embedded_worker_dispatcher.h
@@ -6,30 +6,19 @@
 #define CONTENT_RENDERER_SERVICE_WORKER_EMBEDDED_WORKER_DISPATCHER_H_
 
 #include <map>
-#include <memory>
 
 #include "base/id_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
-#include "content/child/scoped_child_process_reference.h"
 #include "content/public/common/console_message_level.h"
 #include "ipc/ipc_listener.h"
 
+struct EmbeddedWorkerMsg_StartWorker_Params;
 class GURL;
 
-namespace blink {
-
-class WebEmbeddedWorker;
-
-}  // namespace blink
-
 namespace content {
 
-class EmbeddedWorkerDevToolsAgent;
-class ServiceWorkerContextClient;
-struct EmbeddedWorkerStartParams;
-
 // A tiny dispatcher which handles embedded worker start/stop messages.
 class EmbeddedWorkerDispatcher : public IPC::Listener {
  public:
@@ -42,37 +31,15 @@
   void WorkerContextDestroyed(int embedded_worker_id);
 
  private:
-  friend class EmbeddedWorkerInstanceClientImpl;
+  class WorkerWrapper;
 
-  // A thin wrapper of WebEmbeddedWorker which also adds and releases process
-  // references automatically.
-  class WorkerWrapper {
-   public:
-    WorkerWrapper(blink::WebEmbeddedWorker* worker,
-                  int devtools_agent_route_id);
-    ~WorkerWrapper();
-
-    blink::WebEmbeddedWorker* worker() { return worker_.get(); }
-
-   private:
-    ScopedChildProcessReference process_ref_;
-    std::unique_ptr<blink::WebEmbeddedWorker> worker_;
-    std::unique_ptr<EmbeddedWorkerDevToolsAgent> dev_tools_agent_;
-  };
-
-  void OnStartWorker(const EmbeddedWorkerStartParams& params);
+  void OnStartWorker(const EmbeddedWorkerMsg_StartWorker_Params& params);
   void OnStopWorker(int embedded_worker_id);
   void OnResumeAfterDownload(int embedded_worker_id);
   void OnAddMessageToConsole(int embedded_worker_id,
                              ConsoleMessageLevel level,
                              const std::string& message);
 
-  std::unique_ptr<WorkerWrapper> StartWorkerContext(
-      const EmbeddedWorkerStartParams& params,
-      std::unique_ptr<ServiceWorkerContextClient> context_client);
-  void RegisterWorker(int embedded_worker_id,
-                      std::unique_ptr<WorkerWrapper> wrapper);
-
   IDMap<WorkerWrapper, IDMapOwnPointer> workers_;
   std::map<int /* embedded_worker_id */, base::TimeTicks> stop_worker_times_;
   base::WeakPtrFactory<EmbeddedWorkerDispatcher> weak_factory_;
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
deleted file mode 100644
index 1d045dd..0000000
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
-
-#include <memory>
-
-#include "base/strings/utf_string_conversions.h"
-#include "content/child/scoped_child_process_reference.h"
-#include "content/common/service_worker/embedded_worker_messages.h"
-#include "content/public/common/content_client.h"
-#include "content/renderer/service_worker/embedded_worker_devtools_agent.h"
-#include "content/renderer/service_worker/service_worker_context_client.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "third_party/WebKit/public/web/WebEmbeddedWorker.h"
-#include "third_party/WebKit/public/web/WebEmbeddedWorkerStartData.h"
-
-namespace content {
-
-namespace {
-
-void OnError() {
-  // TODO(shimazu): Implement here
-  NOTIMPLEMENTED();
-}
-
-}  // namespace
-
-// static
-void EmbeddedWorkerInstanceClientImpl::Create(
-    EmbeddedWorkerDispatcher* dispatcher,
-    mojo::InterfaceRequest<mojom::EmbeddedWorkerInstanceClient> request) {
-  auto binding = mojo::MakeStrongBinding(
-      base::MakeUnique<EmbeddedWorkerInstanceClientImpl>(dispatcher),
-      std::move(request));
-  binding->set_connection_error_handler(base::Bind(&OnError));
-}
-
-void EmbeddedWorkerInstanceClientImpl::StartWorker(
-    const EmbeddedWorkerStartParams& params) {
-  TRACE_EVENT0("ServiceWorker",
-               "EmbeddedWorkerInstanceClientImpl::StartWorker");
-
-  std::unique_ptr<EmbeddedWorkerDispatcher::WorkerWrapper> wrapper =
-      dispatcher_->StartWorkerContext(
-          params,
-          base::MakeUnique<ServiceWorkerContextClient>(
-              params.embedded_worker_id, params.service_worker_version_id,
-              params.scope, params.script_url,
-              params.worker_devtools_agent_route_id));
-  wrapper_ = wrapper.get();
-  dispatcher_->RegisterWorker(params.embedded_worker_id, std::move(wrapper));
-}
-
-EmbeddedWorkerInstanceClientImpl::EmbeddedWorkerInstanceClientImpl(
-    EmbeddedWorkerDispatcher* dispatcher)
-    : dispatcher_(dispatcher) {}
-
-EmbeddedWorkerInstanceClientImpl::~EmbeddedWorkerInstanceClientImpl() {}
-
-}  // namespace content
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
deleted file mode 100644
index 893ae1d..0000000
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 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.
-
-#ifndef CONTENT_RENDERER_SERVICE_WORKER_EMBEDDED_WORKER_INSTANCE_CLIENT_IMPL_H_
-#define CONTENT_RENDERER_SERVICE_WORKER_EMBEDDED_WORKER_INSTANCE_CLIENT_IMPL_H_
-
-#include "base/id_map.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/renderer/service_worker/embedded_worker_dispatcher.h"
-
-namespace content {
-
-class EmbeddedWorkerInstanceClientImpl
-    : public mojom::EmbeddedWorkerInstanceClient {
- public:
-  static void Create(
-      EmbeddedWorkerDispatcher* dispatcher,
-      mojo::InterfaceRequest<mojom::EmbeddedWorkerInstanceClient> request);
-
-  explicit EmbeddedWorkerInstanceClientImpl(
-      EmbeddedWorkerDispatcher* dispatcher);
-  ~EmbeddedWorkerInstanceClientImpl() override;
-
-  // Implementation of mojo interface
-  void StartWorker(const EmbeddedWorkerStartParams& params) override;
-
- private:
-  EmbeddedWorkerDispatcher* dispatcher_;
-
-  EmbeddedWorkerDispatcher::WorkerWrapper* wrapper_;
-
-  DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceClientImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_SERVICE_WORKER_EMBEDDED_WORKER_INSTANCE_CLIENT_IMPL_H_
diff --git a/content/test/data/media/getusermedia.html b/content/test/data/media/getusermedia.html
index f066a8a..e045635 100644
--- a/content/test/data/media/getusermedia.html
+++ b/content/test/data/media/getusermedia.html
@@ -76,7 +76,7 @@
     navigator.webkitGetUserMedia(
         constraints,
         function(stream) {
-          var duplicate = new webkitMediaStream(stream);
+          var duplicate = new MediaStream(stream);
           assertEquals(stream.getVideoTracks().length, 1);
           assertEquals(duplicate.getVideoTracks().length, 1);
           assertEquals(stream.getVideoTracks()[0].id,
@@ -95,7 +95,7 @@
     navigator.webkitGetUserMedia(
         constraints,
         function(stream) {
-          var duplicate = new webkitMediaStream();
+          var duplicate = new MediaStream();
           duplicate.addTrack(stream.getVideoTracks()[0]);
           assertEquals(duplicate.getVideoTracks().length, 1);
           assertEquals(duplicate.getVideoTracks().length, 1);
@@ -115,7 +115,7 @@
     navigator.webkitGetUserMedia(
         constraints,
         function(stream) {
-          var duplicate = new webkitMediaStream();
+          var duplicate = new MediaStream();
           duplicate.addTrack(stream.getVideoTracks()[0].clone());
           assertEquals(duplicate.getVideoTracks().length, 1);
           assertEquals(duplicate.getVideoTracks().length, 1);
@@ -368,7 +368,7 @@
   function createAndRenderClone(stream) {
     // TODO(perkj):  --use-fake-device-for-media-stream do not currently
     // work with audio devices and not all bots has a microphone.
-    newStream = new webkitMediaStream();
+    newStream = new MediaStream();
     newStream.addTrack(stream.getVideoTracks()[0]);
     assertEquals(newStream.getVideoTracks().length, 1);
     if (stream.getAudioTracks().length > 0) {
diff --git a/content/test/data/media/peerconnection-call.html b/content/test/data/media/peerconnection-call.html
index b285bc1..c592ab2 100644
--- a/content/test/data/media/peerconnection-call.html
+++ b/content/test/data/media/peerconnection-call.html
@@ -134,7 +134,7 @@
 
     detectVideoPlaying('remote-view-2', function() {
       // Construct a new media stream with remote tracks.
-      var newStream = new webkitMediaStream();
+      var newStream = new MediaStream();
       newStream.addTrack(
           gSecondConnection.getRemoteStreams()[0].getAudioTracks()[0]);
       newStream.addTrack(
@@ -447,7 +447,7 @@
     if (typeof localStream.clone === "function") {
       clonedStream = localStream.clone();
     } else {
-      clonedStream = new webkitMediaStream(localStream);
+      clonedStream = new MediaStream(localStream);
     }
 
     gFirstConnection.addStream(localStream);
@@ -481,7 +481,7 @@
   // added to both peer connections.
   function createNewVideoStreamAndAddToBothConnections(localStream) {
     displayAndRemember(localStream);
-    var newStream = new webkitMediaStream();
+    var newStream = new MediaStream();
     newStream.addTrack(localStream.getVideoTracks()[0]);
     gFirstConnection.addStream(newStream);
     gSecondConnection.addStream(newStream);
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 4b7bf23..fc54dc7 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -60,6 +60,7 @@
     "bluetooth/test/test_bluetooth_local_gatt_service_delegate.cc",
     "bluetooth/test/test_bluetooth_local_gatt_service_delegate.h",
     "gamepad/gamepad_provider_unittest.cc",
+    "generic_sensor/platform_sensor_provider_unittest.cc",
     "test/run_all_unittests.cc",
   ]
 
@@ -71,6 +72,8 @@
     "//device/bluetooth:mocks",
     "//device/gamepad",
     "//device/gamepad:test_helpers",
+    "//device/generic_sensor",
+    "//device/generic_sensor:testing",
     "//device/geolocation:unittests",
     "//device/power_save_blocker",
     "//mojo/common",
diff --git a/device/generic_sensor/BUILD.gn b/device/generic_sensor/BUILD.gn
index 75bed03..0e10af42 100644
--- a/device/generic_sensor/BUILD.gn
+++ b/device/generic_sensor/BUILD.gn
@@ -61,3 +61,19 @@
     ]
   }
 }
+
+static_library("testing") {
+  testonly = true
+  sources = [
+    "fake_platform_sensor.cc",
+    "fake_platform_sensor.h",
+    "fake_platform_sensor_provider.cc",
+    "fake_platform_sensor_provider.h",
+  ]
+
+  public_deps = [
+    ":generic_sensor",
+    "//base",
+    "//device/generic_sensor/public/interfaces",
+  ]
+}
diff --git a/device/generic_sensor/fake_platform_sensor.cc b/device/generic_sensor/fake_platform_sensor.cc
new file mode 100644
index 0000000..dd21afc
--- /dev/null
+++ b/device/generic_sensor/fake_platform_sensor.cc
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/generic_sensor/fake_platform_sensor.h"
+
+namespace device {
+
+FakePlatformSensor::FakePlatformSensor(mojom::SensorType type,
+                                       mojo::ScopedSharedBufferMapping mapping,
+                                       uint64_t buffer_size,
+                                       PlatformSensorProvider* provider)
+    : PlatformSensor(type, std::move(mapping), provider), started_(false) {}
+
+FakePlatformSensor::~FakePlatformSensor() = default;
+
+mojom::ReportingMode FakePlatformSensor::GetReportingMode() {
+  return mojom::ReportingMode::CONTINUOUS;
+}
+
+PlatformSensorConfiguration FakePlatformSensor::GetDefaultConfiguration() {
+  return PlatformSensorConfiguration();
+}
+
+bool FakePlatformSensor::StartSensor(
+    const PlatformSensorConfiguration& configuration) {
+  config_ = configuration;
+  started_ = true;
+  return started_;
+}
+
+void FakePlatformSensor::StopSensor() {
+  started_ = false;
+}
+
+bool FakePlatformSensor::CheckSensorConfiguration(
+    const PlatformSensorConfiguration& configuration) {
+  return configuration.frequency() <= kMaxFrequencyValueForTests;
+}
+
+}  // namespace device
diff --git a/device/generic_sensor/fake_platform_sensor.h b/device/generic_sensor/fake_platform_sensor.h
new file mode 100644
index 0000000..1f571fbb
--- /dev/null
+++ b/device/generic_sensor/fake_platform_sensor.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef DEVICE_GENERIC_SENSOR_FAKE_PLATFORM_SENSOR_H_
+#define DEVICE_GENERIC_SENSOR_FAKE_PLATFORM_SENSOR_H_
+
+#include "device/generic_sensor/platform_sensor.h"
+
+namespace device {
+
+class FakePlatformSensor : public PlatformSensor {
+ public:
+  FakePlatformSensor(mojom::SensorType type,
+                     mojo::ScopedSharedBufferMapping mapping,
+                     uint64_t buffer_size,
+                     PlatformSensorProvider* provider);
+
+  mojom::ReportingMode GetReportingMode() override;
+  PlatformSensorConfiguration GetDefaultConfiguration() override;
+
+  bool started() const { return started_; }
+
+  using PlatformSensor::NotifySensorReadingChanged;
+  using PlatformSensor::NotifySensorError;
+  using PlatformSensor::config_map;
+
+ protected:
+  ~FakePlatformSensor() override;
+  bool StartSensor(const PlatformSensorConfiguration& configuration) override;
+  void StopSensor() override;
+  bool CheckSensorConfiguration(
+      const PlatformSensorConfiguration& configuration) override;
+
+ private:
+  static constexpr double kMaxFrequencyValueForTests = 50.0;
+
+  PlatformSensorConfiguration config_;
+
+  bool started_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakePlatformSensor);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_GENERIC_SENSOR_FAKE_PLATFORM_SENSOR_H
diff --git a/device/generic_sensor/fake_platform_sensor_provider.cc b/device/generic_sensor/fake_platform_sensor_provider.cc
new file mode 100644
index 0000000..920ea5d9
--- /dev/null
+++ b/device/generic_sensor/fake_platform_sensor_provider.cc
@@ -0,0 +1,30 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/singleton.h"
+
+#include "device/generic_sensor/fake_platform_sensor.h"
+#include "device/generic_sensor/fake_platform_sensor_provider.h"
+
+namespace device {
+
+// static
+FakePlatformSensorProvider* FakePlatformSensorProvider::GetInstance() {
+  return base::Singleton<
+      FakePlatformSensorProvider,
+      base::LeakySingletonTraits<FakePlatformSensorProvider>>::get();
+}
+
+FakePlatformSensorProvider::FakePlatformSensorProvider() = default;
+
+FakePlatformSensorProvider::~FakePlatformSensorProvider() = default;
+
+scoped_refptr<PlatformSensor> FakePlatformSensorProvider::CreateSensorInternal(
+    mojom::SensorType type,
+    mojo::ScopedSharedBufferMapping mapping,
+    uint64_t buffer_size) {
+  return new FakePlatformSensor(type, std::move(mapping), buffer_size, this);
+}
+
+}  // namespace device
diff --git a/device/generic_sensor/fake_platform_sensor_provider.h b/device/generic_sensor/fake_platform_sensor_provider.h
new file mode 100644
index 0000000..ea2178b
--- /dev/null
+++ b/device/generic_sensor/fake_platform_sensor_provider.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef DEVICE_GENERIC_SENSOR_FAKE_PLATFORM_SENSOR_PROVIDER_H_
+#define DEVICE_GENERIC_SENSOR_FAKE_PLATFORM_SENSOR_PROVIDER_H_
+
+#include "device/generic_sensor/platform_sensor_provider.h"
+
+namespace device {
+
+class FakePlatformSensorProvider : public PlatformSensorProvider {
+ public:
+  FakePlatformSensorProvider();
+  ~FakePlatformSensorProvider() override;
+
+  static FakePlatformSensorProvider* GetInstance();
+
+ protected:
+  scoped_refptr<PlatformSensor> CreateSensorInternal(
+      mojom::SensorType type,
+      mojo::ScopedSharedBufferMapping mapping,
+      uint64_t buffer_size) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakePlatformSensorProvider);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_GENERIC_SENSOR_MOCK_PLATFORM_SENSOR_PROVIDER_H_
diff --git a/device/generic_sensor/platform_sensor.h b/device/generic_sensor/platform_sensor.h
index 0f26380..0485fc6 100644
--- a/device/generic_sensor/platform_sensor.h
+++ b/device/generic_sensor/platform_sensor.h
@@ -69,6 +69,9 @@
 
   mojo::ScopedSharedBufferMapping shared_buffer_mapping_;
 
+  // For testing purposes.
+  const ConfigMap& config_map() const { return config_map_; }
+
  private:
   friend class base::RefCountedThreadSafe<PlatformSensor>;
 
diff --git a/device/generic_sensor/platform_sensor_provider_unittest.cc b/device/generic_sensor/platform_sensor_provider_unittest.cc
new file mode 100644
index 0000000..b63b6f85
--- /dev/null
+++ b/device/generic_sensor/platform_sensor_provider_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/generic_sensor/fake_platform_sensor.h"
+#include "device/generic_sensor/fake_platform_sensor_provider.h"
+#include "device/generic_sensor/public/interfaces/sensor_provider.mojom.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace device {
+
+using mojom::SensorInitParams;
+using mojom::SensorType;
+
+namespace {
+
+uint64_t GetBufferOffset(mojom::SensorType type) {
+  return (static_cast<uint64_t>(SensorType::LAST) -
+          static_cast<uint64_t>(type)) *
+         SensorInitParams::kReadBufferSize;
+}
+
+}  // namespace
+
+class PlatformSensorTestClient : public PlatformSensor::Client {
+ public:
+  PlatformSensorTestClient()
+      : notification_suspended_(false),
+        sensor_reading_changed_(false),
+        sensor_error_(false) {}
+
+  ~PlatformSensorTestClient() override {
+    if (sensor_)
+      sensor_->RemoveClient(this);
+  }
+
+  // PlatformSensor::Client override.
+  void OnSensorReadingChanged() override { sensor_reading_changed_ = true; }
+
+  void OnSensorError() override { sensor_error_ = true; }
+
+  bool IsNotificationSuspended() override { return notification_suspended_; }
+
+  void set_notification_suspended(bool value) {
+    notification_suspended_ = value;
+  }
+
+  void SetSensor(scoped_refptr<PlatformSensor> sensor) {
+    sensor_ = sensor;
+    sensor_->AddClient(this);
+  }
+
+  bool sensor_reading_changed() const { return sensor_reading_changed_; }
+
+  bool sensor_error() const { return sensor_error_; }
+
+ private:
+  scoped_refptr<PlatformSensor> sensor_;
+  bool notification_suspended_;
+  bool sensor_reading_changed_;
+  bool sensor_error_;
+};
+
+class PlatformSensorProviderTest : public ::testing::Test {
+ public:
+  PlatformSensorProviderTest()
+      : sensor_client_(new PlatformSensorTestClient()) {}
+
+ protected:
+  scoped_refptr<PlatformSensor> CreateSensor(mojom::SensorType type) {
+    return FakePlatformSensorProvider::GetInstance()->CreateSensor(
+        type, SensorInitParams::kReadBufferSize, GetBufferOffset(type));
+  }
+
+  std::unique_ptr<PlatformSensorTestClient> sensor_client_;
+};
+
+TEST_F(PlatformSensorProviderTest, CreateSensorsAndCheckType) {
+  scoped_refptr<PlatformSensor> sensor1 =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_TRUE(sensor1);
+  EXPECT_EQ(SensorType::AMBIENT_LIGHT, sensor1->GetType());
+
+  scoped_refptr<PlatformSensor> sensor2 = CreateSensor(SensorType::PROXIMITY);
+  EXPECT_TRUE(sensor2);
+  EXPECT_EQ(SensorType::PROXIMITY, sensor2->GetType());
+
+  scoped_refptr<PlatformSensor> sensor3 =
+      CreateSensor(SensorType::ACCELEROMETER);
+  EXPECT_TRUE(sensor3);
+  EXPECT_EQ(SensorType::ACCELEROMETER, sensor3->GetType());
+
+  scoped_refptr<PlatformSensor> sensor4 = CreateSensor(SensorType::GYROSCOPE);
+  EXPECT_TRUE(sensor4);
+  EXPECT_EQ(SensorType::GYROSCOPE, sensor4->GetType());
+
+  scoped_refptr<PlatformSensor> sensor5 = CreateSensor(SensorType::PRESSURE);
+  EXPECT_TRUE(sensor5);
+  EXPECT_EQ(SensorType::PRESSURE, sensor5->GetType());
+}
+
+TEST_F(PlatformSensorProviderTest, CreateAndGetSensor) {
+  PlatformSensorProvider* sensor_provider =
+      FakePlatformSensorProvider::GetInstance();
+
+  // Create Ambient Light sensor.
+  scoped_refptr<PlatformSensor> sensor1 =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_TRUE(sensor1);
+  EXPECT_EQ(SensorType::AMBIENT_LIGHT, sensor1->GetType());
+
+  // Try to get Gyroscope sensor, which has not been created yet.
+  scoped_refptr<PlatformSensor> sensor2 =
+      sensor_provider->GetSensor(SensorType::GYROSCOPE);
+  EXPECT_FALSE(sensor2);
+
+  // Get Ambient Light sensor.
+  scoped_refptr<PlatformSensor> sensor3 =
+      sensor_provider->GetSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_TRUE(sensor3);
+
+  EXPECT_EQ(sensor1->GetType(), sensor3->GetType());
+
+  // Try to create a sensor with zero buffer and offset.
+  scoped_refptr<PlatformSensor> sensor4 =
+      sensor_provider->CreateSensor(SensorType::GYROSCOPE, 0, 0);
+  EXPECT_FALSE(sensor4);
+
+  scoped_refptr<PlatformSensor> sensor5 =
+      sensor_provider->GetSensor(SensorType::GYROSCOPE);
+  EXPECT_FALSE(sensor5);
+}
+
+TEST_F(PlatformSensorProviderTest, TestSensorLeaks) {
+  PlatformSensorProvider* sensor_provider =
+      FakePlatformSensorProvider::GetInstance();
+
+  // Create Ambient Light sensor.
+  scoped_refptr<PlatformSensor> sensor1 =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_TRUE(sensor1);
+  EXPECT_EQ(SensorType::AMBIENT_LIGHT, sensor1->GetType());
+
+  // Sensor should be automatically destroyed.
+  sensor1 = nullptr;
+  scoped_refptr<PlatformSensor> sensor2 =
+      sensor_provider->GetSensor(SensorType::AMBIENT_LIGHT);
+  EXPECT_FALSE(sensor2);
+}
+
+// This test assumes that a mock sensor has a constant maximum frequency value
+// of 50 hz (different from the base sensor class that has a range from 0 to
+// 60) and tests whether a mock sensor can be started with a value range from 0
+// to 60.
+TEST_F(PlatformSensorProviderTest, StartListeningWithDifferentParameters) {
+  const double too_high_frequency = 60;
+  const double normal_frequency = 39;
+  scoped_refptr<PlatformSensor> sensor =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  FakePlatformSensor* fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor.get());
+  EXPECT_TRUE(fake_sensor);
+  sensor_client_->SetSensor(sensor);
+
+  PlatformSensorConfiguration config(too_high_frequency);
+  EXPECT_EQ(too_high_frequency, config.frequency());
+  EXPECT_FALSE(fake_sensor->StartListening(sensor_client_.get(), config));
+  EXPECT_FALSE(fake_sensor->started());
+
+  config.set_frequency(normal_frequency);
+  EXPECT_EQ(normal_frequency, config.frequency());
+  EXPECT_TRUE(fake_sensor->StartListening(sensor_client_.get(), config));
+  EXPECT_TRUE(fake_sensor->started());
+
+  EXPECT_TRUE(fake_sensor->StopListening(sensor_client_.get(), config));
+  EXPECT_FALSE(fake_sensor->started());
+}
+
+// If a client is in a suspended mode, a NotifySensorReadingChanged()
+// notification must not be sent to the client but NotifySensorError() must be.
+TEST_F(PlatformSensorProviderTest, TestNotificationSuspended) {
+  const int num = 5;
+  scoped_refptr<PlatformSensor> sensor = CreateSensor(SensorType::GYROSCOPE);
+  FakePlatformSensor* fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor.get());
+
+  std::vector<std::unique_ptr<PlatformSensorTestClient>> clients;
+  for (int i = 0; i < num; i++) {
+    std::unique_ptr<PlatformSensorTestClient> client(
+        new PlatformSensorTestClient());
+    client->SetSensor(fake_sensor);
+    clients.push_back(std::move(client));
+  }
+
+  clients.front()->set_notification_suspended(true);
+  fake_sensor->NotifySensorReadingChanged();
+  fake_sensor->NotifySensorError();
+  for (auto const& client : clients) {
+    if (client == clients.front()) {
+      EXPECT_FALSE(client->sensor_reading_changed());
+      EXPECT_TRUE(client->sensor_error());
+      continue;
+    }
+    EXPECT_TRUE(client->sensor_reading_changed());
+    EXPECT_TRUE(client->sensor_error());
+  }
+
+  clients.front()->set_notification_suspended(false);
+  fake_sensor->NotifySensorReadingChanged();
+  fake_sensor->NotifySensorError();
+  for (auto const& client : clients) {
+    EXPECT_TRUE(client->sensor_reading_changed());
+    EXPECT_TRUE(client->sensor_error());
+  }
+}
+
+// Tests that when all clients are removed, config maps are removed as well.
+TEST_F(PlatformSensorProviderTest, TestAddRemoveClients) {
+  const int num = 5;
+
+  scoped_refptr<PlatformSensor> sensor =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  FakePlatformSensor* fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor.get());
+  EXPECT_TRUE(fake_sensor->config_map().empty());
+
+  std::vector<std::unique_ptr<PlatformSensorTestClient>> clients;
+  PlatformSensorConfiguration config(30);
+  for (int i = 0; i < num; i++) {
+    std::unique_ptr<PlatformSensorTestClient> client(
+        new PlatformSensorTestClient());
+    client->SetSensor(fake_sensor);
+    EXPECT_TRUE(fake_sensor->StartListening(client.get(), config));
+    EXPECT_TRUE(fake_sensor->started());
+
+    clients.push_back(std::move(client));
+  }
+  EXPECT_FALSE(fake_sensor->config_map().empty());
+
+  for (const auto& client : clients)
+    fake_sensor->RemoveClient(client.get());
+
+  EXPECT_TRUE(fake_sensor->config_map().empty());
+}
+
+// Tests a sensor cannot be updated if it has one suspended client.
+TEST_F(PlatformSensorProviderTest, TestUpdateSensorOneClient) {
+  scoped_refptr<PlatformSensor> sensor =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  FakePlatformSensor* fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor.get());
+  EXPECT_TRUE(fake_sensor->config_map().empty());
+
+  sensor_client_->SetSensor(fake_sensor);
+
+  PlatformSensorConfiguration config(30);
+  fake_sensor->StartListening(sensor_client_.get(), config);
+
+  sensor_client_->set_notification_suspended(true);
+  EXPECT_TRUE(sensor_client_->IsNotificationSuspended());
+
+  fake_sensor->UpdateSensor();
+  EXPECT_FALSE(fake_sensor->started());
+
+  sensor_client_->set_notification_suspended(false);
+  EXPECT_FALSE(sensor_client_->IsNotificationSuspended());
+
+  fake_sensor->UpdateSensor();
+  EXPECT_TRUE(fake_sensor->started());
+}
+
+// Tests a sensor can be updated if it has one suspended client and other
+// clients are not suspended.
+TEST_F(PlatformSensorProviderTest, TestUpdateSensorManyClients) {
+  const int num = 5;
+
+  scoped_refptr<PlatformSensor> sensor =
+      CreateSensor(SensorType::AMBIENT_LIGHT);
+  FakePlatformSensor* fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor.get());
+  EXPECT_TRUE(fake_sensor->config_map().empty());
+
+  sensor_client_->SetSensor(fake_sensor);
+  std::vector<std::unique_ptr<PlatformSensorTestClient>> clients;
+  for (int i = 0; i < num; i++) {
+    std::unique_ptr<PlatformSensorTestClient> client(
+        new PlatformSensorTestClient());
+    client->SetSensor(fake_sensor);
+    clients.push_back(std::move(client));
+  }
+
+  double sensor_frequency = 30;
+  PlatformSensorConfiguration config(sensor_frequency++);
+  fake_sensor->StartListening(sensor_client_.get(), config);
+  for (const auto& client : clients) {
+    PlatformSensorConfiguration config(sensor_frequency++);
+    fake_sensor->StartListening(client.get(), config);
+  }
+
+  sensor_client_->set_notification_suspended(true);
+  EXPECT_TRUE(sensor_client_->IsNotificationSuspended());
+  for (const auto& client : clients)
+    EXPECT_FALSE(client->IsNotificationSuspended());
+
+  fake_sensor->UpdateSensor();
+  EXPECT_TRUE(fake_sensor->started());
+
+  sensor_client_->set_notification_suspended(false);
+  EXPECT_FALSE(sensor_client_->IsNotificationSuspended());
+  for (const auto& client : clients)
+    EXPECT_FALSE(client->IsNotificationSuspended());
+
+  fake_sensor->UpdateSensor();
+  EXPECT_TRUE(fake_sensor->started());
+}
+
+}  // namespace device
diff --git a/device/generic_sensor/public/cpp/platform_sensor_configuration.cc b/device/generic_sensor/public/cpp/platform_sensor_configuration.cc
index 4061ad0..44b26b3 100644
--- a/device/generic_sensor/public/cpp/platform_sensor_configuration.cc
+++ b/device/generic_sensor/public/cpp/platform_sensor_configuration.cc
@@ -4,16 +4,25 @@
 
 #include "device/generic_sensor/public/cpp/platform_sensor_configuration.h"
 
+#include "device/generic_sensor/public/interfaces/sensor.mojom.h"
+
 namespace device {
 
 PlatformSensorConfiguration::PlatformSensorConfiguration(double frequency)
     : frequency_(frequency) {
-  DCHECK(frequency_ <= kMaxAllowedFrequency && frequency_ > 0.0);
+  DCHECK(frequency_ <= mojom::SensorConfiguration::kMaxAllowedFrequency &&
+         frequency_ > 0.0);
 }
 
 PlatformSensorConfiguration::PlatformSensorConfiguration() = default;
 PlatformSensorConfiguration::~PlatformSensorConfiguration() = default;
 
+void PlatformSensorConfiguration::set_frequency(double frequency) {
+  DCHECK(frequency_ <= mojom::SensorConfiguration::kMaxAllowedFrequency &&
+         frequency_ > 0.0);
+  frequency_ = frequency;
+}
+
 bool PlatformSensorConfiguration::operator==(
     const PlatformSensorConfiguration& other) const {
   return frequency_ == other.frequency();
diff --git a/device/generic_sensor/public/cpp/platform_sensor_configuration.h b/device/generic_sensor/public/cpp/platform_sensor_configuration.h
index b8fe0005..0e08e452 100644
--- a/device/generic_sensor/public/cpp/platform_sensor_configuration.h
+++ b/device/generic_sensor/public/cpp/platform_sensor_configuration.h
@@ -22,15 +22,9 @@
   // used to compare two configurations.
   virtual bool operator>(const PlatformSensorConfiguration& other) const;
 
-  void set_frequency(double frequency) {
-    DCHECK(frequency_ <= kMaxAllowedFrequency && frequency_ > 0.0);
-    frequency_ = frequency;
-  }
-
+  void set_frequency(double frequency);
   double frequency() const { return frequency_; }
 
-  static constexpr double kMaxAllowedFrequency = 60.0;
-
  private:
   double frequency_ = 1.0;  // 1 Hz by default.
 };
diff --git a/device/generic_sensor/public/cpp/sensor_struct_traits.cc b/device/generic_sensor/public/cpp/sensor_struct_traits.cc
index f3ce3c4..2a36547 100644
--- a/device/generic_sensor/public/cpp/sensor_struct_traits.cc
+++ b/device/generic_sensor/public/cpp/sensor_struct_traits.cc
@@ -13,7 +13,7 @@
          device::PlatformSensorConfiguration* out) {
   // Maximum allowed frequency is capped to 60Hz.
   if (data.frequency() >
-          device::PlatformSensorConfiguration::kMaxAllowedFrequency ||
+          device::mojom::SensorConfiguration::kMaxAllowedFrequency ||
       data.frequency() <= 0.0) {
     return false;
   }
diff --git a/device/generic_sensor/public/interfaces/sensor.mojom b/device/generic_sensor/public/interfaces/sensor.mojom
index 74f57d4..be71501 100644
--- a/device/generic_sensor/public/interfaces/sensor.mojom
+++ b/device/generic_sensor/public/interfaces/sensor.mojom
@@ -30,7 +30,10 @@
 };
 
 struct SensorConfiguration {
-  // Requested frequency in Hz (max is 60 Hz).
+  // Maximum allowed frequency is 60 Hz.
+  const double kMaxAllowedFrequency = 60.0;
+
+  // Requested frequency in Hz.
   double frequency;
   // TODO(shalamov): Add map<string, union> for sensor specific configuration.
 };
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 9b060a8..e2412b7 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -92,35 +92,6 @@
   }],
   "bluetoothLowEnergy": {
     "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"]
-  },
-  "bluetoothLowEnergy.createService": {
-    "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"],
-    "platforms": ["chromeos", "linux"]
-  },
-  "bluetoothLowEnergy.createCharacteristic": {
-    "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"],
-    "platforms": ["chromeos", "linux"]
-  },
-  "bluetoothLowEnergy.createDescriptor": {
-    "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"],
-    "platforms": ["chromeos", "linux"]
-  },
-  "bluetoothLowEnergy.registerService": {
-    "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"],
-    "platforms": ["chromeos", "linux"]
-  },
-  "bluetoothLowEnergy.unregisterService": {
-    "dependencies": ["manifest:bluetooth"],
-    "contexts": ["blessed_extension"],
-    "platforms": ["chromeos", "linux"]
-  },
-  "bluetoothLowEnergy.sendRequestResponse": {
-    "dependencies": ["manifest:bluetooth"],
     "contexts": ["blessed_extension"],
     "platforms": ["chromeos", "linux"]
   },
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index ae654fe..acbcdec9a 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -4801,10 +4801,6 @@
 
   GURL requestURL = net::GURLWithNSURL(action.request.URL);
 
-  // Don't create web views for invalid URLs.
-  if (!requestURL.is_valid())
-    return nil;
-
   if (![self userIsInteracting]) {
     NSString* referer = [self refererFromNavigationAction:action];
     GURL referrerURL =
diff --git a/ppapi/examples/video_effects/video_effects.html b/ppapi/examples/video_effects/video_effects.html
index f7dd0ce..fa37d59 100644
--- a/ppapi/examples/video_effects/video_effects.html
+++ b/ppapi/examples/video_effects/video_effects.html
@@ -92,7 +92,7 @@
   // Call into getUserMedia via the polyfill (adapter.js).
   getUserMedia({audio:false, video:true},
                 gotStream, function() {});
-}  
+}
 
 function onRegisterStreamDone() {
   vidprocessedlocal.src = URL.createObjectURL(processedLocalstream);
@@ -111,7 +111,7 @@
 
 function initEffect() {
   var url = URL.createObjectURL(localstream);
-  processedLocalstream = new webkitMediaStream([]);
+  processedLocalstream = new MediaStream([]);
   var processedStreamUrl = URL.createObjectURL(processedLocalstream);
   effectsPlugin.postMessage(
       'registerStream' + ' ' + url + ' ' + processedStreamUrl);
@@ -127,7 +127,7 @@
     effectsPlugin.postMessage('effectOff');
   }
 }
-    
+
 function call() {
   callButton.disabled = true;
   hangupButton.disabled = false;
@@ -135,15 +135,15 @@
   var servers = null;
   pc1 = new RTCPeerConnection(servers);
   trace("Created local peer connection object pc1");
-  pc1.onicecandidate = iceCallback1; 
+  pc1.onicecandidate = iceCallback1;
   pc2 = new RTCPeerConnection(servers);
   trace("Created remote peer connection object pc2");
   pc2.onicecandidate = iceCallback2;
-  pc2.onaddstream = gotRemoteStream; 
+  pc2.onaddstream = gotRemoteStream;
 
   pc1.addStream(processedLocalstream);
   trace("Adding Local Stream to peer connection");
-  
+
   pc1.createOffer(gotDescription1);
 }
 
@@ -155,7 +155,7 @@
   // to pass in the right constraints in order for it to
   // accept the incoming offer of audio and video.
   var sdpConstraints = {'mandatory': {
-                        'OfferToReceiveAudio':true, 
+                        'OfferToReceiveAudio':true,
                         'OfferToReceiveVideo':true }};
   pc2.createAnswer(gotDescription2, null, sdpConstraints);
 }
@@ -168,7 +168,7 @@
 
 function hangup() {
   trace("Ending call");
-  pc1.close(); 
+  pc1.close();
   pc2.close();
   pc1 = null;
   pc2 = null;
@@ -187,7 +187,7 @@
     trace("Local ICE candidate: \n" + event.candidate.candidate);
   }
 }
-      
+
 function iceCallback2(event){
   if (event.candidate) {
     pc1.addIceCandidate(new RTCIceCandidate(event.candidate));
@@ -204,5 +204,3 @@
 </script>
 </body>
 </html>
-
-
diff --git a/ppapi/tests/test_video_destination.cc b/ppapi/tests/test_video_destination.cc
index f3c4cf8a..367b002 100644
--- a/ppapi/tests/test_video_destination.cc
+++ b/ppapi/tests/test_video_destination.cc
@@ -100,7 +100,7 @@
 
 std::string TestVideoDestination::TestPutFrame() {
   std::string js_code;
-  js_code += "var test_stream = new webkitMediaStream([]);"
+  js_code += "var test_stream = new MediaStream([]);"
              "var url = URL.createObjectURL(test_stream);"
              "var plugin = document.getElementById('plugin');"
              "plugin.postMessage(url);";
@@ -124,4 +124,3 @@
 
   PASS();
 }
-
diff --git a/testing/android/native_test/BUILD.gn b/testing/android/native_test/BUILD.gn
index 971c423..f08a8d1 100644
--- a/testing/android/native_test/BUILD.gn
+++ b/testing/android/native_test/BUILD.gn
@@ -41,7 +41,6 @@
     "//testing/android/appurify_support:appurify_support_java",
     "//testing/android/reporter:reporter_java",
   ]
-  srcjar_deps = [ "//base:base_native_libraries_gen" ]
   java_files = [
     "java/src/org/chromium/native_test/NativeBrowserTestActivity.java",
     "java/src/org/chromium/native_test/NativeTest.java",
@@ -50,7 +49,6 @@
     "java/src/org/chromium/native_test/NativeUnitTestActivity.java",
     "java/src/org/chromium/native_test/NativeUnitTestNativeActivity.java",
   ]
-  jar_excluded_patterns = [ "*/NativeLibraries.class" ]
 }
 
 generate_jni("native_test_jni_headers") {
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
index 425874f..3a716c0a 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.browser_tests.filter
@@ -60,6 +60,7 @@
 -TemplateURLScraperTest.ScrapeWithOnSubmit
 -ThreatDOMDetailsTest.Everything
 -TracingBrowserTest.TestBackgroundMemoryInfra
+-TracingBrowserTest.TestMemoryInfra
 -WebBluetoothTest.WebBluetoothAfterCrash
 -WebNavigationApiTest.Crash
 -WebNavigationApiTest.CrossProcess
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index e2ca33c5..123773b 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -84,7 +84,7 @@
     "args": [],
   },
   "android_junit_tests": {
-    "label": "//webrtc:android_juint_tests",
+    "label": "//webrtc:android_junit_tests",
     "type": "junit_test",
   },
   "android_webview_test_apk": {
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 6d75989..ea0760b 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -288,10 +288,5 @@
     "prefix": "enable_wasm",
     "base": "http/tests/wasm",
     "args": ["--enable-wasm"]
-  },
-  {
-    "prefix": "mojo-service-worker",
-    "base": "http/tests/serviceworker",
-    "args": ["--mojo-service-worker"]
   }
 ]
diff --git a/third_party/WebKit/LayoutTests/fast/events/constructors/media-stream-event-constructor.html b/third_party/WebKit/LayoutTests/fast/events/constructors/media-stream-event-constructor.html
index a7038a59..fb643a9 100644
--- a/third_party/WebKit/LayoutTests/fast/events/constructors/media-stream-event-constructor.html
+++ b/third_party/WebKit/LayoutTests/fast/events/constructors/media-stream-event-constructor.html
@@ -9,7 +9,7 @@
 description("This tests the constructor for the MediaStreamEvent DOM class.");
 
 var testObject = {nyannyan: 123};
-var mediaStream = new webkitMediaStream();
+var mediaStream = new MediaStream();
 
 // No initializer is passed.
 shouldBe("new MediaStreamEvent('eventType').bubbles", "false");
diff --git a/third_party/WebKit/LayoutTests/fast/js/constructor-length-expected.txt b/third_party/WebKit/LayoutTests/fast/js/constructor-length-expected.txt
index 309a8ab..67a9b8e 100644
--- a/third_party/WebKit/LayoutTests/fast/js/constructor-length-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/js/constructor-length-expected.txt
@@ -23,7 +23,7 @@
 PASS Int32Array.length is 3
 PASS Int8Array.length is 3
 FAIL Intent.length should be 3. Threw exception ReferenceError: Intent is not defined
-FAIL MediaStream.length should be 0. Threw exception ReferenceError: MediaStream is not defined
+PASS MediaStream.length is 0
 PASS MessageChannel.length is 0
 PASS MessageEvent.length is 1
 PASS PageTransitionEvent.length is 1
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStream-onactive-oninactive.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStream-onactive-oninactive.html
index 0abcbb4..7d58e79b 100644
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStream-onactive-oninactive.html
+++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStream-onactive-oninactive.html
@@ -32,7 +32,7 @@
 }
 
 function gotStream2(s) {
-    stream = new webkitMediaStream();
+    stream = new MediaStream();
     shouldBeFalse('stream.active');
     stream.onactive = streamActive;
     stream.addTrack(s.getAudioTracks()[0]);
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor-expected.txt
index 2a56ded9..ee3f5df0 100644
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor-expected.txt
@@ -1,21 +1,26 @@
-Tests webkitMediaStream.
+Tests MediaStream constructor.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS Got local stream.
+Interface tests.
+PASS typeof MediaStream is 'function'
+PASS MediaStream.length is 0
+PASS MediaStream.name is 'MediaStream'
+PASS Object.getPrototypeOf(MediaStream.prototype) is EventTarget.prototype
+PASS window.MediaStream is window.webkitMediaStream
+Got local stream.
 PASS localStream.getAudioTracks().length is 1
 PASS localStream.getVideoTracks().length is 1
-PASS typeof webkitMediaStream === 'function' is true
 PASS checkIdAttribute(localStream.id) is true
-PASS new webkitMediaStream(document) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
-PASS new webkitMediaStream([document]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
-PASS new webkitMediaStream([stream.getAudioTracks()[0], document]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
-PASS new webkitMediaStream([null]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
-PASS new webkitMediaStream([undefined]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
-PASS new webkitMediaStream(null) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
-PASS new webkitMediaStream(undefined) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
-PASS Stream constructed
+PASS new MediaStream(document) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
+PASS new MediaStream([document]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
+PASS new MediaStream([stream.getAudioTracks()[0], document]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
+PASS new MediaStream([null]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
+PASS new MediaStream([undefined]) threw exception TypeError: Failed to construct 'MediaStream': Invalid Array element type.
+PASS new MediaStream(null) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
+PASS new MediaStream(undefined) threw exception TypeError: Failed to construct 'MediaStream': No matching constructor signature..
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -23,7 +28,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is false
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -31,7 +36,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is false
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -39,7 +44,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is true
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -47,7 +52,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is true
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -55,7 +60,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is true
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -63,7 +68,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is true
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -71,7 +76,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is true
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -79,7 +84,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is false
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -87,7 +92,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is false
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
@@ -95,7 +100,7 @@
 PASS newStream.getVideoTracks().length is nVideo
 PASS newStream.active is false
 PASS checkIdAttribute(newStream.id) is true
-PASS Stream constructed
+Stream constructed
 PASS [object MediaStream] is non-null.
 PASS [object MediaStream] is defined.
 PASS newStream.constructor.name is 'MediaStream'
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor.html
index 0f64a87..a09852d0 100644
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor.html
+++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamConstructor.html
@@ -7,7 +7,7 @@
 <p id="description"></p>
 <div id="console"></div>
 <script>
-description("Tests webkitMediaStream.");
+description("Tests MediaStream constructor.");
 
 var undefined;
 var stream;
@@ -45,6 +45,16 @@
     finishJSTest();
 }
 
+function testInterfaces() {
+  debug('Interface tests.');
+  shouldBe('typeof MediaStream', "'function'");
+  shouldBe('MediaStream.length', '0');
+  shouldBe('MediaStream.name', "'MediaStream'");
+  shouldBe('Object.getPrototypeOf(MediaStream.prototype)', 'EventTarget.prototype');
+  // webkitMediaStream is an alias of MediaStream
+  shouldBe('window.MediaStream', 'window.webkitMediaStream');
+}
+
 function getUserMedia(dictionary, callback) {
     try {
         navigator.webkitGetUserMedia(dictionary, callback, error);
@@ -56,40 +66,39 @@
 
 function gotStream(s) {
     localStream = s;
-    testPassed('Got local stream.');
+    debug('Got local stream.');
     shouldBe('localStream.getAudioTracks().length', '1');
     shouldBe('localStream.getVideoTracks().length', '1');
 
-    shouldBeTrue("typeof webkitMediaStream === 'function'");
     shouldBeTrue('checkIdAttribute(localStream.id)');
     testConstructor(localStream);
 }
 
 function testConstructor(s) {
     stream = s;
-    shouldThrow('new webkitMediaStream(document)');
-    shouldThrow('new webkitMediaStream([document])');
-    shouldThrow('new webkitMediaStream([stream.getAudioTracks()[0], document])');
-    shouldThrow('new webkitMediaStream([null])');
-    shouldThrow('new webkitMediaStream([undefined])');
-    shouldThrow('new webkitMediaStream(null)');
-    shouldThrow('new webkitMediaStream(undefined)');
+    shouldThrow('new MediaStream(document)');
+    shouldThrow('new MediaStream([document])');
+    shouldThrow('new MediaStream([stream.getAudioTracks()[0], document])');
+    shouldThrow('new MediaStream([null])');
+    shouldThrow('new MediaStream([undefined])');
+    shouldThrow('new MediaStream(null)');
+    shouldThrow('new MediaStream(undefined)');
 
-    verifyStream(new webkitMediaStream(), 0, 0);
-    verifyStream(new webkitMediaStream([]), 0, 0);
+    verifyStream(new MediaStream(), 0, 0);
+    verifyStream(new MediaStream([]), 0, 0);
 
-    verifyStream(new webkitMediaStream(s), s.getAudioTracks().length, s.getVideoTracks().length);
+    verifyStream(new MediaStream(s), s.getAudioTracks().length, s.getVideoTracks().length);
 
-    verifyStream(new webkitMediaStream([s.getAudioTracks()[0]]), 1, 0);
-    verifyStream(new webkitMediaStream([s.getVideoTracks()[0]]), 0, 1);
-    verifyStream(new webkitMediaStream([s.getAudioTracks()[0], s.getVideoTracks()[0]]), 1, 1);
-    verifyStream(new webkitMediaStream([s.getVideoTracks()[0], s.getAudioTracks()[0], s.getVideoTracks()[0]]), 1, 1);
+    verifyStream(new MediaStream([s.getAudioTracks()[0]]), 1, 0);
+    verifyStream(new MediaStream([s.getVideoTracks()[0]]), 0, 1);
+    verifyStream(new MediaStream([s.getAudioTracks()[0], s.getVideoTracks()[0]]), 1, 1);
+    verifyStream(new MediaStream([s.getVideoTracks()[0], s.getAudioTracks()[0], s.getVideoTracks()[0]]), 1, 1);
 
     s.oninactive = function () {
-        verifyStream(new webkitMediaStream([s.getAudioTracks()[0]]), 0, 0);
-        verifyStream(new webkitMediaStream([s.getVideoTracks()[0]]), 0, 0);
-        verifyStream(new webkitMediaStream([s.getAudioTracks()[0], s.getVideoTracks()[0]]), 0, 0);
-        verifyStream(new webkitMediaStream(s), 0, 0);
+        verifyStream(new MediaStream([s.getAudioTracks()[0]]), 0, 0);
+        verifyStream(new MediaStream([s.getVideoTracks()[0]]), 0, 0);
+        verifyStream(new MediaStream([s.getAudioTracks()[0], s.getVideoTracks()[0]]), 0, 0);
+        verifyStream(new MediaStream(s), 0, 0);
 
         finishJSTest();
     };
@@ -103,10 +112,10 @@
     nAudio = numAudioTracks;
     nVideo = numVideoTracks;
 
-    testPassed('Stream constructed');
+    debug('Stream constructed');
     shouldBeNonNull(newStream);
     shouldBeDefined(newStream);
-    shouldBe("newStream.constructor.name", "'MediaStream'");
+    shouldBe('newStream.constructor.name', "'MediaStream'");
     shouldBe('newStream.getAudioTracks().length', 'nAudio');
     shouldBe('newStream.getVideoTracks().length', 'nVideo');
 
@@ -118,6 +127,7 @@
     shouldBeTrue('checkIdAttribute(newStream.id)');
 }
 
+testInterfaces();
 getUserMedia({video:true, audio:true}, gotStream);
 
 window.jsTestIsAsync = true;
diff --git a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getStats-promise.html b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getStats-promise.html
index 731a63a..661c72a 100644
--- a/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getStats-promise.html
+++ b/third_party/WebKit/LayoutTests/fast/peerconnection/RTCPeerConnection-getStats-promise.html
@@ -9,6 +9,40 @@
 <script>
 var pc = new webkitRTCPeerConnection(null);
 
+function stringify_equals(d1, d2) {
+  return JSON.stringify(d1) == JSON.stringify(d2);
+}
+
+function assert_expected_report(report) {
+  assert_true(report != null);
+  var statsCount = 0;
+  report.forEach(function(s) {
+    assert_true(s != null);
+    statsCount++;
+    assert_true(stringify_equals(s, report.get(s.id)));
+  });
+
+  // Verify that the report contains the same stats that
+  // |MockWebRTCPeerConnectionHandler::getStats| should produce.
+  assert_equals(statsCount, 1);
+  var stats = report.get('mock-stats-01');
+  assert_true(stats != null);
+  assert_equals(stats.type, 'mock-stats');
+  assert_equals(stats.timestamp, 1234.0);
+  assert_equals(stats.int32, 42);
+  assert_equals(stats.uint32, 42);
+  assert_equals(stats.int64, 42);
+  assert_equals(stats.uint64, 42);
+  assert_equals(stats.double, 42);
+  assert_equals(stats.string, '42');
+  assert_true(stringify_equals(stats.sequenceInt32, [42]));
+  assert_true(stringify_equals(stats.sequenceUint32, [42]));
+  assert_true(stringify_equals(stats.sequenceInt64, [42]));
+  assert_true(stringify_equals(stats.sequenceUint64, [42]));
+  assert_true(stringify_equals(stats.sequenceDouble, [42]));
+  assert_true(stringify_equals(stats.sequenceString, ['42']));
+}
+
 promise_test(function() {
   return navigator.mediaDevices.getUserMedia({audio:true, video:true})
     .then(function(mediaStream) {
@@ -16,11 +50,8 @@
         var selector = pc.getLocalStreams()[0].getVideoTracks()[0];
         assert_not_equals(selector, null);
         return pc.getStats(selector)
-          .then(function(statsReport) {
-              assert_unreached('The promise should be rejected.');
-            }, function(reason) {
-              // Expecting promise to be rejected until getStats is implemented.
-              // crbug.com/627816
+          .then(function(report) {
+              assert_expected_report(report);
             });
       });
 }, 'getStats(MediaStreamTrack selector)');
@@ -30,11 +61,8 @@
     .then(function(mediaStream) {
         pc.addStream(mediaStream);
         return pc.getStats(null)
-          .then(function(statsReport) {
-              assert_unreached('The promise should be rejected.');
-            }, function(reason) {
-              // Expecting promise to be rejected until getStats is implemented.
-              // crbug.com/627816
+          .then(function(report) {
+              assert_expected_report(report);
             });
       });
 }, 'getStats(null)');
@@ -44,11 +72,8 @@
     .then(function(mediaStream) {
         pc.addStream(mediaStream);
         return pc.getStats()
-          .then(function(statsReport) {
-              assert_unreached('The promise should be rejected.');
-            }, function(reason) {
-              // Expecting promise to be rejected until getStats is implemented.
-              // crbug.com/627816
+          .then(function(report) {
+              assert_expected_report(report);
             });
       });
 }, 'getStats()');
diff --git a/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-hide-on-click.html b/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-hide-on-click.html
new file mode 100644
index 0000000..a1e7dd6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/video-controls-overflow-menu-hide-on-click.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>Ensure overflow menu buttons are visible when expected.</title>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="media-controls.js"></script>
+<script src="media-file.js"></script>
+<script src="overflow-menu.js"></script>
+
+<!--Padding ensures the overflow menu is visible for the tests. -->
+<body style="padding-top: 200px; padding-left: 100px">
+<video controls></video>
+<script>
+async_test(function(t) {
+  // Set up video
+  var video = document.querySelector("video");
+  video.src = findMediaFile("video", "content/test");
+  video.setAttribute("width", "60");
+  // Add captions
+  var track = video.addTextTrack("captions");
+  // Pretend we have a cast device
+  internals.mediaPlayerRemoteRouteAvailabilityChanged(video, true);
+
+  video.onloadeddata = t.step_func_done(function() {
+    var overflowList = getOverflowList(video);
+    var overflowMenu = getOverflowMenuButton(video);
+
+    // Clicking on the overflow menu button should make the overflow list visible
+    var coords = elementCoordinates(overflowMenu);
+    clickAtCoordinates(coords[0], coords[1]);
+    assert_not_equals(getComputedStyle(overflowList).display, "none");
+
+    // Click on an overflow menu item should close overflow list.
+    var coords = elementCoordinates(overflowList);
+    clickAtCoordinates(coords[0], coords[1]);
+    assert_equals(getComputedStyle(overflowList).display, "none");
+  });
+});
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/sensor/ambient-light-sensor.html b/third_party/WebKit/LayoutTests/sensor/ambient-light-sensor.html
index 340cd57..00fbba5 100644
--- a/third_party/WebKit/LayoutTests/sensor/ambient-light-sensor.html
+++ b/third_party/WebKit/LayoutTests/sensor/ambient-light-sensor.html
@@ -17,6 +17,37 @@
   buffer[1] = kDefaultReadingValue;
 }
 
+test(() => assert_throws(
+    new RangeError(),
+    () => new AmbientLightSensor({frequency: -60})),
+    'Test that negative frequency causes exception from constructor.');
+
+sensor_test(sensor => {
+  let ambientLightSensor = new AmbientLightSensor({frequency: 560});
+  ambientLightSensor.start();
+
+  let testPromise = sensor.mockSensorProvider.getCreatedSensor()
+      .then(mockSensor => { return mockSensor.addConfigurationCalled(); })
+      .then(mockSensor => {
+         return new Promise((resolve, reject) => {
+          ambientLightSensor.onstatechange = event => {
+            if (ambientLightSensor.state === 'idle') {
+              resolve(mockSensor);
+            }
+
+            if (ambientLightSensor.state === 'active') {
+              let configuration = mockSensor.active_sensor_configurations_[0];
+              assert_equals(configuration.frequency, 60);
+              ambientLightSensor.stop();
+            }
+          };
+        });
+      })
+      .then(mockSensor => { return mockSensor.removeConfigurationCalled(); });
+
+  return testPromise;
+}, 'Test that frequency is capped to 60.0 Hz.');
+
 sensor_test(sensor => {
   let ambientLightSensor = new AmbientLightSensor({frequency: 60});
   ambientLightSensor.start();
diff --git a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
index 6eb0f8d..34d523c 100644
--- a/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
+++ b/third_party/WebKit/LayoutTests/sensor/resources/sensor-helpers.js
@@ -49,10 +49,6 @@
       addConfiguration(configuration) {
         assert_not_equals(configuration, null, "Invalid sensor configuration.");
 
-        if (this.add_configuration_called_ != null) {
-          this.add_configuration_called_(this);
-        }
-
         if (!this.start_should_fail_ && this.update_reading_function_ != null) {
           let timeout = (1 / configuration.frequency) * 1000;
           this.sensor_reading_timer_id_ = window.setTimeout(() => {
@@ -61,10 +57,13 @@
               this.client_.sensorReadingChanged();
             }
           }, timeout);
-
-          this.active_sensor_configurations_.push(configuration);
         }
 
+        this.active_sensor_configurations_.push(configuration);
+
+        if (this.add_configuration_called_ != null)
+          this.add_configuration_called_(this);
+
         return sensorResponse(!this.start_should_fail_);
       }
 
diff --git a/third_party/WebKit/LayoutTests/virtual/mojo-service-worker/http/tests/serviceworker/README.txt b/third_party/WebKit/LayoutTests/virtual/mojo-service-worker/http/tests/serviceworker/README.txt
deleted file mode 100644
index 0310ec9..0000000
--- a/third_party/WebKit/LayoutTests/virtual/mojo-service-worker/http/tests/serviceworker/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-This directory is for testing the service workers with mojo by using '--mojo-service-worker' flag.
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index c1ffa3d1..319602f 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -3237,6 +3237,26 @@
     setter onsourceclose
     setter onsourceended
     setter onsourceopen
+interface MediaStream : EventTarget
+    attribute @@toStringTag
+    getter active
+    getter id
+    getter onactive
+    getter onaddtrack
+    getter oninactive
+    getter onremovetrack
+    method addTrack
+    method clone
+    method constructor
+    method getAudioTracks
+    method getTrackById
+    method getTracks
+    method getVideoTracks
+    method removeTrack
+    setter onactive
+    setter onaddtrack
+    setter oninactive
+    setter onremovetrack
 interface MediaStreamAudioDestinationNode : AudioNode
     attribute @@toStringTag
     getter stream
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index f9f31ac9..acb1388 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -3878,6 +3878,26 @@
     setter onsourceclose
     setter onsourceended
     setter onsourceopen
+interface MediaStream : EventTarget
+    attribute @@toStringTag
+    getter active
+    getter id
+    getter onactive
+    getter onaddtrack
+    getter oninactive
+    getter onremovetrack
+    method addTrack
+    method clone
+    method constructor
+    method getAudioTracks
+    method getTrackById
+    method getTracks
+    method getVideoTracks
+    method removeTrack
+    setter onactive
+    setter onaddtrack
+    setter oninactive
+    setter onremovetrack
 interface MediaStreamAudioDestinationNode : AudioNode
     attribute @@toStringTag
     getter stream
@@ -4711,6 +4731,16 @@
     method toJSON
     setter sdp
     setter type
+interface RTCStatsReport
+    attribute @@toStringTag
+    method @@iterator
+    method constructor
+    method entries
+    method forEach
+    method get
+    method has
+    method keys
+    method values
 interface RadioNodeList : NodeList
     attribute @@toStringTag
     getter value
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8PerformanceObserverCallback.h b/third_party/WebKit/Source/bindings/core/v8/V8PerformanceObserverCallback.h
index d44516a..19d1d7e2 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8PerformanceObserverCallback.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8PerformanceObserverCallback.h
@@ -26,13 +26,6 @@
     DECLARE_VIRTUAL_TRACE();
 
     void handleEvent(PerformanceObserverEntryList*, PerformanceObserver*) override;
-
-    // TODO(lkawai,bashi): This function should be removed.
-    ExecutionContext* getExecutionContext() const override
-    {
-        NOTREACHED();
-        return nullptr;
-    }
 private:
     CORE_EXPORT V8PerformanceObserverCallback(v8::Local<v8::Function>, v8::Local<v8::Object>, ScriptState*);
 
diff --git a/third_party/WebKit/Source/core/css/SelectorFilter.cpp b/third_party/WebKit/Source/core/css/SelectorFilter.cpp
index deb464e4..07381af 100644
--- a/third_party/WebKit/Source/core/css/SelectorFilter.cpp
+++ b/third_party/WebKit/Source/core/css/SelectorFilter.cpp
@@ -45,8 +45,12 @@
     if (element.isStyledElement() && element.hasClass()) {
         const SpaceSplitString& classNames = element.classNames();
         size_t count = classNames.size();
-        for (size_t i = 0; i < count; ++i)
-            identifierHashes.append(classNames[i].impl()->existingHash() * ClassAttributeSalt);
+        for (size_t i = 0; i < count; ++i) {
+            DCHECK(classNames[i].impl());
+            // Speculative fix for https://crbug.com/646026
+            if (classNames[i].impl())
+                identifierHashes.append(classNames[i].impl()->existingHash() * ClassAttributeSalt);
+        }
     }
 }
 
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.cpp
index c04fa5c..a234fe8 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.cpp
@@ -182,6 +182,15 @@
     return false;
 }
 
+void MediaControlInputElement::defaultEventHandler(Event* event)
+{
+    // If the element is in the overflow menu, clicking should hide the menu.
+    if (mediaControls().overflowMenuVisible() && parentElement()->shadowPseudoId() == AtomicString("-internal-media-controls-overflow-menu-list-item"))
+        mediaControls().toggleOverflowMenu();
+
+    HTMLInputElement::defaultEventHandler(event);
+}
+
 HTMLElement* MediaControlInputElement::createOverflowElement(MediaControls& mediaControls, MediaControlInputElement* button)
 {
     if (!button)
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.h b/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.h
index 651d05c..8975478 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.h
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElementTypes.h
@@ -177,6 +177,7 @@
 
 protected:
     MediaControlInputElement(MediaControls&, MediaControlElementType);
+    void defaultEventHandler(Event*) override;
 
 private:
     virtual void updateDisplayType() { }
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
index 0acc5c44..557e254 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
@@ -295,7 +295,7 @@
         event->setDefaultHandled();
     }
 
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 void MediaControlMuteButtonElement::updateDisplayType()
@@ -345,7 +345,7 @@
         updateDisplayType();
         event->setDefaultHandled();
     }
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 void MediaControlPlayButtonElement::updateDisplayType()
@@ -424,16 +424,12 @@
 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
 {
     if (event->type() == EventTypeNames::click) {
-        // If the user opens up the closed captions menu from the overfow menu,
-        // the overflow menu should no longer be visible.
-        if (mediaControls().overflowMenuVisible())
-            mediaControls().toggleOverflowMenu();
         mediaControls().toggleTextTrackList();
         updateDisplayType();
         event->setDefaultHandled();
     }
 
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 WebLocalizedString::Name MediaControlToggleClosedCaptionsButtonElement::getOverflowStringName()
@@ -607,7 +603,7 @@
         event->setDefaultHandled();
     }
 
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 // ----------------------------
@@ -862,7 +858,7 @@
         }
         event->setDefaultHandled();
     }
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
@@ -913,7 +909,7 @@
             mediaElement().requestRemotePlayback();
         }
     }
-    HTMLInputElement::defaultEventHandler(event);
+    MediaControlInputElement::defaultEventHandler(event);
 }
 
 const AtomicString& MediaControlCastButtonElement::shadowPseudoId() const
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
index d3fea6a4..a51eb6f 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp
@@ -844,11 +844,12 @@
 
 bool MediaControls::overflowMenuVisible()
 {
-    return m_overflowList->isWanted();
+    return m_overflowList ? m_overflowList->isWanted() : false;
 }
 
 void MediaControls::toggleOverflowMenu()
 {
+    DCHECK(m_overflowList);
     m_overflowList->setIsWanted(!m_overflowList->isWanted());
 }
 
diff --git a/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp b/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
index 7ceb3ed..75de4ad 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceBaseTest.cpp
@@ -42,7 +42,6 @@
     ~MockPerformanceObserverCallback() {}
 
     void handleEvent(PerformanceObserverEntryList*, PerformanceObserver*) override {}
-    ExecutionContext* getExecutionContext() const override { return nullptr; }
 
     DEFINE_INLINE_TRACE()
     {
diff --git a/third_party/WebKit/Source/core/timing/PerformanceObserverCallback.h b/third_party/WebKit/Source/core/timing/PerformanceObserverCallback.h
index 02190b6..12170a7c 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceObserverCallback.h
+++ b/third_party/WebKit/Source/core/timing/PerformanceObserverCallback.h
@@ -19,7 +19,6 @@
     virtual ~PerformanceObserverCallback() { }
 
     virtual void handleEvent(PerformanceObserverEntryList*, PerformanceObserver*) = 0;
-    virtual ExecutionContext* getExecutionContext() const = 0;
 
     DEFINE_INLINE_VIRTUAL_TRACE() { }
 };
diff --git a/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp b/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
index 96293d89..0e3307d 100644
--- a/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
+++ b/third_party/WebKit/Source/core/timing/PerformanceObserverTest.cpp
@@ -20,7 +20,6 @@
     ~MockPerformanceObserverCallback() override {}
 
     void handleEvent(PerformanceObserverEntryList*, PerformanceObserver*) override {}
-    ExecutionContext* getExecutionContext() const override { return nullptr; }
 };
 
 class MockPerformanceBase : public PerformanceBase {
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStream.idl b/third_party/WebKit/Source/modules/mediastream/MediaStream.idl
index 9a6d4b8..5b8fc32 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStream.idl
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStream.idl
@@ -22,15 +22,13 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-// https://w3c.github.io/mediacapture-main/getusermedia.html#idl-def-mediastream
+// https://w3c.github.io/mediacapture-main/#idl-def-mediastream
 
-// TODO(foolip): Unprefix the MediaStream interface. https://crbug.com/649331
 [
     Constructor,
     Constructor(MediaStream stream),
     Constructor(sequence<MediaStreamTrack> tracks),
     ConstructorCallWith=ExecutionContext,
-    NoInterfaceObject,
 ] interface MediaStream : EventTarget {
     readonly attribute DOMString id;
     sequence<MediaStreamTrack> getAudioTracks();
diff --git a/third_party/WebKit/Source/modules/modules_idl_files.gni b/third_party/WebKit/Source/modules/modules_idl_files.gni
index 0daf053..0a704a7 100644
--- a/third_party/WebKit/Source/modules/modules_idl_files.gni
+++ b/third_party/WebKit/Source/modules/modules_idl_files.gni
@@ -185,6 +185,7 @@
                     "peerconnection/RTCSessionDescription.idl",
                     "peerconnection/RTCSessionDescriptionCallback.idl",
                     "peerconnection/RTCStatsCallback.idl",
+                    "peerconnection/RTCStatsReport.idl",
                     "peerconnection/RTCStatsResponse.idl",
                     "permissions/Permissions.idl",
                     "permissions/PermissionStatus.idl",
diff --git a/third_party/WebKit/Source/modules/peerconnection/BUILD.gn b/third_party/WebKit/Source/modules/peerconnection/BUILD.gn
index cf344b8..874572ea 100644
--- a/third_party/WebKit/Source/modules/peerconnection/BUILD.gn
+++ b/third_party/WebKit/Source/modules/peerconnection/BUILD.gn
@@ -32,6 +32,8 @@
     "RTCSessionDescriptionRequestImpl.h",
     "RTCSessionDescriptionRequestPromiseImpl.cpp",
     "RTCSessionDescriptionRequestPromiseImpl.h",
+    "RTCStatsReport.cpp",
+    "RTCStatsReport.h",
     "RTCStatsRequestImpl.cpp",
     "RTCStatsRequestImpl.h",
     "RTCStatsResponse.cpp",
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
index e465510..e04627b2 100644
--- a/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCPeerConnection.cpp
@@ -69,6 +69,7 @@
 #include "modules/peerconnection/RTCSessionDescriptionRequestImpl.h"
 #include "modules/peerconnection/RTCSessionDescriptionRequestPromiseImpl.h"
 #include "modules/peerconnection/RTCStatsCallback.h"
+#include "modules/peerconnection/RTCStatsReport.h"
 #include "modules/peerconnection/RTCStatsRequestImpl.h"
 #include "modules/peerconnection/RTCVoidRequestImpl.h"
 #include "modules/peerconnection/RTCVoidRequestPromiseImpl.h"
@@ -373,6 +374,29 @@
     return rtcOfferOptions;
 }
 
+// Helper class for |RTCPeerConnection::getStats(ScriptState*, MediaStreamTrack*)|
+class WebRTCStatsReportCallbackResolver : public WebRTCStatsReportCallback {
+public:
+    // Takes ownership of |resolver|.
+    static std::unique_ptr<WebRTCStatsReportCallback> create(ScriptPromiseResolver* resolver)
+    {
+        return std::unique_ptr<WebRTCStatsReportCallback>(new WebRTCStatsReportCallbackResolver(resolver));
+    }
+
+    ~WebRTCStatsReportCallbackResolver() override {}
+
+private:
+    WebRTCStatsReportCallbackResolver(ScriptPromiseResolver* resolver)
+        : m_resolver(resolver) {}
+
+    void OnStatsDelivered(std::unique_ptr<WebRTCStatsReport> report) override
+    {
+        m_resolver->resolve(new RTCStatsReport(std::move(report)));
+    }
+
+    Persistent<ScriptPromiseResolver> m_resolver;
+};
+
 } // namespace
 
 RTCPeerConnection::EventWrapper::EventWrapper(
@@ -973,8 +997,12 @@
 {
     ExecutionContext* context = scriptState->getExecutionContext();
     UseCounter::count(context, UseCounter::RTCPeerConnectionGetStats);
-    // TODO(hbos): Implement new |getStats| function. crbug.com/627816
-    return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "getStats(optional MediaStreamTrack?) has not been implemented yet."));
+
+    ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
+    ScriptPromise promise = resolver->promise();
+    m_peerHandler->getStats(WebRTCStatsReportCallbackResolver::create(resolver));
+
+    return promise;
 }
 
 RTCDataChannel* RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionState& exceptionState)
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.cpp b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.cpp
new file mode 100644
index 0000000..f31b31d2
--- /dev/null
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.cpp
@@ -0,0 +1,142 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "modules/peerconnection/RTCStatsReport.h"
+
+namespace blink {
+
+namespace {
+
+template<typename T>
+bool addPropertyValue(v8::Local<v8::Object>& v8Object, v8::Isolate* isolate, T name, v8::Local<v8::Value> value)
+{
+    return v8CallBoolean(v8Object->CreateDataProperty(isolate->GetCurrentContext(), v8String(isolate, name), value));
+}
+
+template<typename T>
+bool addPropertySequenceOfNumbers(v8::Local<v8::Object>& v8Object, v8::Isolate* isolate, WebString name, const WebVector<T>& webVector)
+{
+    v8::Local<v8::Array> v8Array = v8::Array::New(isolate, webVector.size());
+    for (size_t i = 0; i < webVector.size(); ++i) {
+        if (!v8CallBoolean(v8Array->CreateDataProperty(isolate->GetCurrentContext(), static_cast<uint32_t>(i), v8::Number::New(isolate, static_cast<double>(webVector[i])))))
+            return false;
+    }
+    return addPropertyValue(v8Object, isolate, name, v8Array);
+}
+
+bool addPropertySequenceOfStrings(v8::Local<v8::Object>& v8Object, v8::Isolate* isolate, WebString name, const WebVector<WebString>& webVector)
+{
+    v8::Local<v8::Array> v8Array = v8::Array::New(isolate, webVector.size());
+    for (size_t i = 0; i < webVector.size(); ++i) {
+        if (!v8CallBoolean(v8Array->CreateDataProperty(isolate->GetCurrentContext(), static_cast<uint32_t>(i), v8String(isolate, webVector[i]))))
+            return false;
+    }
+    return addPropertyValue(v8Object, isolate, name, v8Array);
+}
+
+v8::Local<v8::Value> webRTCStatsToValue(ScriptState* scriptState, const WebRTCStats* stats)
+{
+    v8::Isolate* isolate = scriptState->isolate();
+    v8::Local<v8::Object> v8Object = v8::Object::New(isolate);
+
+    bool success = true;
+    success &= addPropertyValue(v8Object, isolate, "id", v8String(isolate, stats->id()));
+    success &= addPropertyValue(v8Object, isolate, "timestamp", v8::Number::New(isolate, stats->timestamp()));
+    success &= addPropertyValue(v8Object, isolate, "type", v8String(isolate, stats->type()));
+    for (size_t i = 0; i < stats->membersCount() && success; ++i) {
+        std::unique_ptr<WebRTCStatsMember> member = stats->getMember(i);
+        if (!member->isDefined())
+            continue;
+        WebString name = member->name();
+        switch (member->type()) {
+        case WebRTCStatsMemberTypeInt32:
+            success &= addPropertyValue(v8Object, isolate, name, v8::Number::New(isolate, static_cast<double>(member->valueInt32())));
+            break;
+        case WebRTCStatsMemberTypeUint32:
+            success &= addPropertyValue(v8Object, isolate, name, v8::Number::New(isolate, static_cast<double>(member->valueUint32())));
+            break;
+        case WebRTCStatsMemberTypeInt64:
+            success &= addPropertyValue(v8Object, isolate, name, v8::Number::New(isolate, static_cast<double>(member->valueInt64())));
+            break;
+        case WebRTCStatsMemberTypeUint64:
+            success &= addPropertyValue(v8Object, isolate, name, v8::Number::New(isolate, static_cast<double>(member->valueUint64())));
+            break;
+        case WebRTCStatsMemberTypeDouble:
+            success &= addPropertyValue(v8Object, isolate, name, v8::Number::New(isolate, member->valueDouble()));
+            break;
+        case WebRTCStatsMemberTypeString:
+            success &= addPropertyValue(v8Object, isolate, name, v8String(isolate, member->valueString()));
+            break;
+        case WebRTCStatsMemberTypeSequenceInt32:
+            success &= addPropertySequenceOfNumbers(v8Object, isolate, name, member->valueSequenceInt32());
+            break;
+        case WebRTCStatsMemberTypeSequenceUint32:
+            success &= addPropertySequenceOfNumbers(v8Object, isolate, name, member->valueSequenceUint32());
+            break;
+        case WebRTCStatsMemberTypeSequenceInt64:
+            success &= addPropertySequenceOfNumbers(v8Object, isolate, name, member->valueSequenceInt64());
+            break;
+        case WebRTCStatsMemberTypeSequenceUint64:
+            success &= addPropertySequenceOfNumbers(v8Object, isolate, name, member->valueSequenceUint64());
+            break;
+        case WebRTCStatsMemberTypeSequenceDouble:
+            success &= addPropertySequenceOfNumbers(v8Object, isolate, name, member->valueSequenceDouble());
+            break;
+        case WebRTCStatsMemberTypeSequenceString:
+            success &= addPropertySequenceOfStrings(v8Object, isolate, name, member->valueSequenceString());
+            break;
+        default:
+            NOTREACHED();
+        }
+    }
+    if (!success) {
+        NOTREACHED();
+        return v8::Undefined(isolate);
+    }
+    return v8Object;
+}
+
+class RTCStatsReportIterationSource final : public PairIterable<String, v8::Local<v8::Value>>::IterationSource {
+public:
+    RTCStatsReportIterationSource(std::unique_ptr<WebRTCStatsReport> report)
+        : m_report(std::move(report))
+    {
+    }
+
+    bool next(ScriptState* scriptState, String& key, v8::Local<v8::Value>& value, ExceptionState& exceptionState) override
+    {
+        std::unique_ptr<WebRTCStats> stats = m_report->next();
+        if (!stats)
+            return false;
+        key = stats->id();
+        value = webRTCStatsToValue(scriptState, stats.get());
+        return true;
+    }
+
+private:
+    std::unique_ptr<WebRTCStatsReport> m_report;
+};
+
+} // namespace
+
+RTCStatsReport::RTCStatsReport(std::unique_ptr<WebRTCStatsReport> report)
+    : m_report(std::move(report))
+{
+}
+
+PairIterable<String, v8::Local<v8::Value>>::IterationSource* RTCStatsReport::startIteration(ScriptState*, ExceptionState&)
+{
+    return new RTCStatsReportIterationSource(m_report->copyHandle());
+}
+
+bool RTCStatsReport::getMapEntry(ScriptState* scriptState, const String& key, v8::Local<v8::Value>& value, ExceptionState&)
+{
+    std::unique_ptr<WebRTCStats> stats = m_report->getStats(key);
+    if (!stats)
+        return false;
+    value = webRTCStatsToValue(scriptState, stats.get());
+    return true;
+}
+
+} // namespace blink
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.h b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.h
new file mode 100644
index 0000000..e36827a
--- /dev/null
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.h
@@ -0,0 +1,40 @@
+// 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.
+
+#ifndef RTCStatsReport_h
+#define RTCStatsReport_h
+
+#include "bindings/core/v8/Maplike.h"
+#include "bindings/core/v8/ScriptWrappable.h"
+#include "platform/heap/GarbageCollected.h"
+#include "public/platform/WebCString.h"
+#include "public/platform/WebRTCStats.h"
+#include "wtf/text/WTFString.h"
+
+#include <map>
+
+namespace blink {
+
+// https://w3c.github.io/webrtc-pc/#rtcstatsreport-object
+class RTCStatsReport final
+    : public GarbageCollectedFinalized<RTCStatsReport>
+    , public ScriptWrappable
+    , public Maplike<String, v8::Local<v8::Value>> {
+    DEFINE_WRAPPERTYPEINFO();
+public:
+    RTCStatsReport(std::unique_ptr<WebRTCStatsReport>);
+
+    // Maplike<String, v8::Local<v8::Value>>
+    PairIterable<String, v8::Local<v8::Value>>::IterationSource* startIteration(ScriptState*, ExceptionState&) override;
+    bool getMapEntry(ScriptState*, const String& key, v8::Local<v8::Value>&, ExceptionState&) override;
+
+    DEFINE_INLINE_VIRTUAL_TRACE() { }
+
+private:
+    std::unique_ptr<WebRTCStatsReport> m_report;
+};
+
+} // namespace blink
+
+#endif // RTCStatsReport_h
diff --git a/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.idl b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.idl
new file mode 100644
index 0000000..9a6d169
--- /dev/null
+++ b/third_party/WebKit/Source/modules/peerconnection/RTCStatsReport.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// https://w3c.github.io/webrtc-pc/#rtcstatsreport-object
+[
+    RuntimeEnabled=RTCPeerConnectionNewGetStats,
+] interface RTCStatsReport {
+    readonly maplike<DOMString, object>;
+};
diff --git a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.cpp b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.cpp
index aaede9f..fbc298b8 100644
--- a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.cpp
+++ b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.cpp
@@ -13,19 +13,19 @@
 namespace blink {
 
 // static
-AmbientLightSensor* AmbientLightSensor::create(ExecutionContext* context, const SensorOptions& sensorOptions)
+AmbientLightSensor* AmbientLightSensor::create(ScriptState* scriptState, const SensorOptions& options, ExceptionState& exceptionState)
 {
-    return new AmbientLightSensor(context, sensorOptions);
+    return new AmbientLightSensor(scriptState, options, exceptionState);
 }
 
 // static
-AmbientLightSensor* AmbientLightSensor::create(ExecutionContext* context)
+AmbientLightSensor* AmbientLightSensor::create(ScriptState* scriptState, ExceptionState& exceptionState)
 {
-    return create(context, SensorOptions());
+    return create(scriptState, SensorOptions(), exceptionState);
 }
 
-AmbientLightSensor::AmbientLightSensor(ExecutionContext* executionContext, const SensorOptions& sensorOptions)
-    : Sensor(executionContext, sensorOptions, SensorType::AMBIENT_LIGHT)
+AmbientLightSensor::AmbientLightSensor(ScriptState* scriptState, const SensorOptions& options, ExceptionState& exceptionState)
+    : Sensor(scriptState, options, exceptionState, SensorType::AMBIENT_LIGHT)
 {
 }
 
diff --git a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.h b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.h
index ba0660d..7e98ba9 100644
--- a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.h
+++ b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.h
@@ -14,15 +14,15 @@
 class AmbientLightSensor final : public Sensor {
     DEFINE_WRAPPERTYPEINFO();
 public:
-    static AmbientLightSensor* create(ExecutionContext*, const SensorOptions&);
-    static AmbientLightSensor* create(ExecutionContext*);
+    static AmbientLightSensor* create(ScriptState*, const SensorOptions&, ExceptionState&);
+    static AmbientLightSensor* create(ScriptState*, ExceptionState&);
 
     AmbientLightSensorReading* reading() const;
 
     DECLARE_VIRTUAL_TRACE();
 
 private:
-    AmbientLightSensor(ExecutionContext*, const SensorOptions&);
+    AmbientLightSensor(ScriptState*, const SensorOptions&, ExceptionState&);
     // Sensor overrides.
     SensorReading* createSensorReading(SensorProxy*) override;
     SensorConfigurationPtr createSensorConfig(const SensorOptions&, const SensorConfiguration& defaultConfig) override;
diff --git a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.idl b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.idl
index eb3daf8..8d9b829 100644
--- a/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.idl
+++ b/third_party/WebKit/Source/modules/sensor/AmbientLightSensor.idl
@@ -8,7 +8,8 @@
 [
     RuntimeEnabled=Sensor,
     Constructor(optional SensorOptions sensorOptions),
-    ConstructorCallWith=ExecutionContext,
+    ConstructorCallWith=ScriptState,
+    RaisesException=Constructor,
 ] interface AmbientLightSensor : Sensor {
     readonly attribute AmbientLightSensorReading? reading;
 };
diff --git a/third_party/WebKit/Source/modules/sensor/Sensor.cpp b/third_party/WebKit/Source/modules/sensor/Sensor.cpp
index 872afd78..d5f1b04 100644
--- a/third_party/WebKit/Source/modules/sensor/Sensor.cpp
+++ b/third_party/WebKit/Source/modules/sensor/Sensor.cpp
@@ -6,6 +6,7 @@
 
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
+#include "core/inspector/ConsoleMessage.h"
 #include "device/generic_sensor/public/interfaces/sensor.mojom-blink.h"
 #include "modules/sensor/SensorErrorEvent.h"
 #include "modules/sensor/SensorPollingStrategy.h"
@@ -17,15 +18,42 @@
 
 namespace blink {
 
-Sensor::Sensor(ExecutionContext* executionContext, const SensorOptions& sensorOptions, SensorType type)
+Sensor::Sensor(ScriptState* scriptState, const SensorOptions& sensorOptions, ExceptionState& exceptionState, SensorType type)
     : ActiveScriptWrappable(this)
-    , ContextLifecycleObserver(executionContext)
-    , PageVisibilityObserver(toDocument(executionContext)->page())
+    , ContextLifecycleObserver(scriptState->getExecutionContext())
+    , PageVisibilityObserver(toDocument(scriptState->getExecutionContext())->page())
     , m_sensorOptions(sensorOptions)
     , m_type(type)
     , m_state(Sensor::SensorState::IDLE)
     , m_storedData()
 {
+    // Check secure context.
+    String errorMessage;
+    if (!scriptState->getExecutionContext()->isSecureContext(errorMessage)) {
+        exceptionState.throwDOMException(SecurityError, errorMessage);
+        return;
+    }
+
+    // Check top-level browsing context.
+    if (!scriptState->domWindow() || !scriptState->domWindow()->frame() || !scriptState->domWindow()->frame()->isMainFrame()) {
+        exceptionState.throwSecurityError("Must be in a top-level browsing context");
+        return;
+    }
+
+    // Check the given frequency value.
+    if (m_sensorOptions.hasFrequency()) {
+        double frequency = m_sensorOptions.frequency();
+        if (frequency <= 0.0) {
+            exceptionState.throwRangeError("Frequency must be positive.");
+            return;
+        }
+
+        if (frequency > SensorConfiguration::kMaxAllowedFrequency) {
+            m_sensorOptions.setFrequency(SensorConfiguration::kMaxAllowedFrequency);
+            ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, InfoMessageLevel, "Frequency is limited to 60 Hz.");
+            scriptState->getExecutionContext()->addConsoleMessage(consoleMessage);
+        }
+    }
 }
 
 Sensor::~Sensor() = default;
diff --git a/third_party/WebKit/Source/modules/sensor/Sensor.h b/third_party/WebKit/Source/modules/sensor/Sensor.h
index cb7cb8b..39c21752 100644
--- a/third_party/WebKit/Source/modules/sensor/Sensor.h
+++ b/third_party/WebKit/Source/modules/sensor/Sensor.h
@@ -64,7 +64,7 @@
     DECLARE_VIRTUAL_TRACE();
 
 protected:
-    Sensor(ExecutionContext*, const SensorOptions&, device::mojom::blink::SensorType);
+    Sensor(ScriptState*, const SensorOptions&, ExceptionState&, device::mojom::blink::SensorType);
     virtual SensorReading* createSensorReading(SensorProxy*) = 0;
 
     using SensorConfigurationPtr = device::mojom::blink::SensorConfigurationPtr;
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
index 49fb816b..a6f3a26c 100644
--- a/tools/perf/benchmarks/dromaeo.py
+++ b/tools/perf/benchmarks/dromaeo.py
@@ -195,7 +195,6 @@
     # http://crbug.com/634055 (Android One).
     return cls.IsSvelte(possible_browser)
 
-@benchmark.Disabled('all')  # crbug.com/650317
 class DromaeoJslibAttrPrototype(_DromaeoBenchmark):
   """Dromaeo JSLib attr prototype JavaScript benchmark.
 
diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc
index eed61df..842307ae 100644
--- a/ui/message_center/notification.cc
+++ b/ui/message_center/notification.cc
@@ -26,6 +26,12 @@
     : title(title) {
 }
 
+ButtonInfo::ButtonInfo(const ButtonInfo& other) = default;
+
+ButtonInfo::~ButtonInfo() = default;
+
+ButtonInfo& ButtonInfo::operator=(const ButtonInfo& other) = default;
+
 RichNotificationData::RichNotificationData()
     : priority(DEFAULT_PRIORITY),
       never_timeout(false),
diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h
index 93d255e..e88555c 100644
--- a/ui/message_center/notification.h
+++ b/ui/message_center/notification.h
@@ -29,11 +29,18 @@
   NotificationItem(const base::string16& title, const base::string16& message);
 };
 
+enum class ButtonType { BUTTON, TEXT };
+
 struct MESSAGE_CENTER_EXPORT ButtonInfo {
   base::string16 title;
   gfx::Image icon;
+  ButtonType type = ButtonType::BUTTON;
+  base::string16 placeholder;
 
-  ButtonInfo(const base::string16& title);
+  explicit ButtonInfo(const base::string16& title);
+  ButtonInfo(const ButtonInfo& other);
+  ~ButtonInfo();
+  ButtonInfo& operator=(const ButtonInfo& other);
 };
 
 class MESSAGE_CENTER_EXPORT RichNotificationData {