diff --git a/DEPS b/DEPS
index 4961c73b..c08423d 100644
--- a/DEPS
+++ b/DEPS
@@ -39,7 +39,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '14184d5567b58085b6d8a6375796d405056f7f73',
+  'skia_revision': '43f610f693d6d5db115d68ecde8e3fd52e725c6a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
diff --git a/WATCHLISTS b/WATCHLISTS
index d28a6a2c..80348f5 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -275,10 +275,6 @@
       'filepath': 'net/cookies/|'\
                   'chrome/browser/net/sqlite_persistent_cookie_store',
     },
-    'custom_handlers': {
-      'filepath': 'chrome/browser/custom_handlers/|'\
-                  'chrome/common/custom_handlers/',
-    },
     'custom_tabs': {
       'filepath': 'chrome/android/java/src/org/chromium/chrome/browser/customtabs/|'\
                   'chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/',
@@ -440,6 +436,9 @@
       'filepath': '_message.*.h|'\
                   '_messages.cc',
     },
+    'login': {
+      'filepath': 'chrome/browser/ui/login'
+    },
     'libwebp': {
       'filepath': 'third_party/libwebp'
     },
@@ -1244,7 +1243,8 @@
     'autofill': ['bondd+autofillwatch@chromium.org',
                  'estade+watch@chromium.org',
                  'jdonnelly+autofillwatch@chromium.org',
-                 'rouslan+autofill@chromium.org'],
+                 'rouslan+autofill@chromium.org',
+                 'vabr+watchlistautofill@chromium.org'],
     'automation': ['robertshield@chromium.org'],
     'background_sync': ['jkarlin+watch@chromium.org'],
     'base': ['vmpstr+watch@chromium.org'],
@@ -1316,7 +1316,6 @@
                       'peter@chromium.org'],
     'content_worker': ['blink-worker-reviews@chromium.org',
                        'kinuko+watch@chromium.org'],
-    'custom_handlers': ['vabr+watchlist@chromium.org'],
     'deep_memory_profiler': ['dmikurube@chromium.org'],
     'device_bluetooth': ['scheib+watch@chromium.org'],
     'device_sensors': ['timvolodine@chromium.org',
@@ -1365,6 +1364,7 @@
                 'jfweitz+watch@chromium.org', 'skanuj+watch@chromium.org'],
     'ios_chrome': ['sdefresne+watch@chromium.org'],
     'ipc': ['jam@chromium.org', 'darin-cc@chromium.org'],
+    'login': ['vabr+watchlistlogin@chromium.org'],
     'libwebp': ['urvang@chromium.org', 'jzern@chromium.org',
                 'skal@google.com', 'vikasa@google.com'],
     'libvpx': ['tomfinegan@chromium.org', 'jzern@chromium.org',
@@ -1420,7 +1420,7 @@
                'dcheng@chromium.org', 'jianli@chromium.org'],
     'password_manager': ['mkwst+watchlist-passwords@chromium.org',
                          'gcasto+watchlist@chromium.org',
-                         'vabr+watchlist@chromium.org'],
+                         'vabr+watchlistpasswordmanager@chromium.org'],
     'pepper_api': ['bradnelson+warch@chromium.org',
                    'piman+watch@chromium.org', 'ihf+watch@chromium.org',
                    'yusukes+watch@chromium.org', 'binji+watch@chromium.org',
diff --git a/android_webview/android_webview_tests.gypi b/android_webview/android_webview_tests.gypi
index 14c90ca..a5af244 100644
--- a/android_webview/android_webview_tests.gypi
+++ b/android_webview/android_webview_tests.gypi
@@ -11,6 +11,8 @@
         'android_webview_java',
         'android_webview_pak',
         'libdrawgl',
+        '../base/base.gyp:base_java_test_support',
+        '../components/components.gyp:policy_java_test_support'
       ],
       'variables': {
         'apk_name': 'AndroidWebView',
@@ -96,6 +98,7 @@
       'type': 'none',
       'dependencies': [
         '../base/base.gyp:base_java_test_support',
+        '../components/components.gyp:policy_java_test_support',
         '../content/content_shell_and_tests.gyp:content_java_test_support',
         '../net/net.gyp:net_java_test_support',
         '../testing/android/on_device_instrumentation.gyp:broker_java',
diff --git a/android_webview/javatests/AndroidManifest.xml b/android_webview/javatests/AndroidManifest.xml
index fb3db24..ed0ae66f 100644
--- a/android_webview/javatests/AndroidManifest.xml
+++ b/android_webview/javatests/AndroidManifest.xml
@@ -5,7 +5,7 @@
   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="org.chromium.android_webview.test">
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23" />
-    <instrumentation android:name="org.chromium.base.test.BaseInstrumentationTestRunner"
+    <instrumentation android:name="org.chromium.android_webview.test.AwInstrumentationTestRunner"
         android:targetPackage="org.chromium.android_webview.shell"
         android:label="Tests for org.chromium.android_webview"/>
     <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
diff --git a/android_webview/javatests/DEPS b/android_webview/javatests/DEPS
index e2a33b1f..24ccfae 100644
--- a/android_webview/javatests/DEPS
+++ b/android_webview/javatests/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+components/external_video_surface/android/java",
   "+components/policy/android/java",
+  "+components/policy/android/javatests",
   "+content/public/android/java",
   "+content/public/test/android/javatests",
 ]
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
index 7c2629e..32bdae7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/PolicyUrlFilteringTest.java
@@ -4,10 +4,8 @@
 
 package org.chromium.android_webview.test;
 
-import android.os.Bundle;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
 import android.util.Pair;
 
 import org.chromium.android_webview.AwContents;
@@ -17,9 +15,13 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.net.test.util.TestWebServer;
+import org.chromium.policy.AbstractAppRestrictionsProvider;
 import org.chromium.policy.CombinedPolicyProvider;
+import org.chromium.policy.test.PolicyData;
+import org.chromium.policy.test.annotations.Policies;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /** Tests for the policy based URL filtering. */
 public class PolicyUrlFilteringTest extends AwTestBase {
@@ -28,7 +30,8 @@
     private TestWebServer mWebServer;
     private String mFooTestUrl;
     private String mBarTestUrl;
-    private AwPolicyProvider mTestProvider;
+    private static final String sFooTestFilePath = "/foo.html";
+    private static final String sFooWhitelistFilter = "localhost" + sFooTestFilePath;
 
     private static final String sBlacklistPolicyName = "com.android.browser:URLBlacklist";
     private static final String sWhitelistPolicyName = "com.android.browser:URLWhitelist";
@@ -36,21 +39,14 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        setTestAwContentsClient(new TestAwContentsClient());
+        mContentsClient = new TestAwContentsClient();
+        mAwContents = createAwTestContainerViewOnMainSync(mContentsClient).getAwContents();
         mWebServer = TestWebServer.start();
-        mFooTestUrl = mWebServer.setResponse("/foo.html", "<html><body>foo</body></html>",
+        mFooTestUrl = mWebServer.setResponse(sFooTestFilePath, "<html><body>foo</body></html>",
                 new ArrayList<Pair<String, String>>());
         mBarTestUrl = mWebServer.setResponse("/bar.html", "<html><body>bar</body></html>",
                 new ArrayList<Pair<String, String>>());
 
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mTestProvider = new AwPolicyProvider(getActivity().getApplicationContext());
-                CombinedPolicyProvider.get().registerProvider(mTestProvider);
-            }
-        });
-
         getInstrumentation().waitForIdleSync();
     }
 
@@ -60,97 +56,83 @@
         super.tearDown();
     }
 
-    private void setTestAwContentsClient(TestAwContentsClient contentsClient) throws Exception {
-        mContentsClient = contentsClient;
-        final AwTestContainerView testContainerView =
-                createAwTestContainerViewOnMainSync(mContentsClient);
-        mAwContents = testContainerView.getAwContents();
-    }
-
     // Tests transforming the bundle to native policies, reloading the policies and blocking
     // the navigation.
     @MediumTest
     @Feature({"AndroidWebView", "Policy"})
     public void testBlacklistedUrl() throws Throwable {
-        TestCallbackHelperContainer.OnReceivedErrorHelper onReceivedErrorHelper =
-                mContentsClient.getOnReceivedErrorHelper();
-        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
-                mContentsClient.getOnPageFinishedHelper();
+        final AwPolicyProvider testProvider =
+                new AwPolicyProvider(getActivity().getApplicationContext());
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                CombinedPolicyProvider.get().registerProvider(testProvider);
+            }
+        });
 
-        loadUrlSync(mAwContents, onPageFinishedHelper, mFooTestUrl);
-        assertEquals(mFooTestUrl, onPageFinishedHelper.getUrl());
-        assertEquals(0, onReceivedErrorHelper.getCallCount());
+        navigateAndCheckOutcome(mFooTestUrl, 0);
 
-        setFilteringPolicy(new String[] {"localhost"}, new String[] {});
+        setFilteringPolicy(testProvider, new String[] {"localhost"}, new String[] {});
 
-        loadUrlSync(mAwContents, onPageFinishedHelper, mFooTestUrl);
-        assertEquals(mFooTestUrl, onPageFinishedHelper.getUrl());
-        assertEquals(1, onReceivedErrorHelper.getCallCount());
-        assertEquals(ErrorCodeConversionHelper.ERROR_CONNECT, onReceivedErrorHelper.getErrorCode());
+        navigateAndCheckOutcome(mFooTestUrl, 1);
+        assertEquals(ErrorCodeConversionHelper.ERROR_CONNECT,
+                mContentsClient.getOnReceivedErrorHelper().getErrorCode());
     }
 
-    // Tests transforming the bundle to native policies, reloading the policies and getting a
-    // successful navigation with a whitelist.
+    // Tests getting a successful navigation with a whitelist.
     @MediumTest
     @Feature({"AndroidWebView", "Policy"})
+    @Policies.Add({
+            @Policies.Item(key = sBlacklistPolicyName, stringArray = {"*"}),
+            @Policies.Item(key = sWhitelistPolicyName, stringArray = {sFooWhitelistFilter})})
     public void testWhitelistedUrl() throws Throwable {
-        TestCallbackHelperContainer.OnReceivedErrorHelper onReceivedErrorHelper =
-                mContentsClient.getOnReceivedErrorHelper();
-        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
-                mContentsClient.getOnPageFinishedHelper();
-        setFilteringPolicy(new String[] {"*"}, new String[] {mFooTestUrl});
-
-        loadUrlSync(mAwContents, onPageFinishedHelper, mFooTestUrl);
-        assertEquals(mFooTestUrl, onPageFinishedHelper.getUrl());
-        assertEquals(0, onReceivedErrorHelper.getCallCount());
+        navigateAndCheckOutcome(mFooTestUrl, 0);
 
         // Make sure it goes through the blacklist
-        loadUrlSync(mAwContents, onPageFinishedHelper, mBarTestUrl);
-        assertEquals(mBarTestUrl, onPageFinishedHelper.getUrl());
-        assertEquals(1, onReceivedErrorHelper.getCallCount());
-        assertEquals(ErrorCodeConversionHelper.ERROR_CONNECT, onReceivedErrorHelper.getErrorCode());
+        navigateAndCheckOutcome(mBarTestUrl, 1);
+        assertEquals(ErrorCodeConversionHelper.ERROR_CONNECT,
+                mContentsClient.getOnReceivedErrorHelper().getErrorCode());
     }
 
     // Tests that bad policy values are properly handled
     @SmallTest
     @Feature({"AndroidWebView", "Policy"})
+    @Policies.Add({
+            @Policies.Item(key = sBlacklistPolicyName, string = "shouldBeAJsonArrayNotAString")})
     public void testBadPolicyValue() throws Exception {
-        final Bundle newPolicies = new Bundle();
-        newPolicies.putString(sBlacklistPolicyName, "shouldBeAJsonArrayNotAString");
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-            @Override
-            public void run() {
-                mTestProvider.notifySettingsAvailable(newPolicies);
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-
-        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
-                mContentsClient.getOnPageFinishedHelper();
-        loadUrlSync(mAwContents, onPageFinishedHelper, mFooTestUrl);
-
+        navigateAndCheckOutcome(mFooTestUrl, 0);
         // At the moment this test is written, a failure is a crash, a success is no crash.
     }
 
-    private void setFilteringPolicy(final String[] blacklistUrls, final String[] whitelistUrls) {
-        final Bundle newPolicies = new Bundle();
+    /**
+     * Synchronously loads the provided URL and checks that the number or reported errors for the
+     * current context is the expected one.
+     */
+    private void navigateAndCheckOutcome(String url, int expectedErrorCount) throws Exception {
+        TestCallbackHelperContainer.OnReceivedErrorHelper onReceivedErrorHelper =
+                mContentsClient.getOnReceivedErrorHelper();
+        TestCallbackHelperContainer.OnPageFinishedHelper onPageFinishedHelper =
+                mContentsClient.getOnPageFinishedHelper();
 
-        if (blacklistUrls != null && blacklistUrls.length > 0) {
-            String blacklistString =
-                    String.format("[\"%s\"]", TextUtils.join("\",\"", blacklistUrls));
-            newPolicies.putString(sBlacklistPolicyName, blacklistString);
-        }
+        loadUrlSync(mAwContents, onPageFinishedHelper, url);
+        assertEquals(url, onPageFinishedHelper.getUrl());
+        assertEquals(expectedErrorCount, onReceivedErrorHelper.getCallCount());
+    }
 
-        if (whitelistUrls != null && whitelistUrls.length > 0) {
-            String whitelistString =
-                    String.format("[\"%s\"]", TextUtils.join("\",\"", whitelistUrls));
-            newPolicies.putString(sWhitelistPolicyName, whitelistString);
-        }
+    private void setFilteringPolicy(final AwPolicyProvider testProvider,
+            final String[] blacklistUrls, final String[] whitelistUrls) {
+        final PolicyData[] policies = {
+            new PolicyData.StrArray(sBlacklistPolicyName, blacklistUrls),
+            new PolicyData.StrArray(sWhitelistPolicyName, whitelistUrls)
+        };
+
+        AbstractAppRestrictionsProvider.setTestRestrictions(
+                PolicyData.asBundle(Arrays.asList(policies)));
 
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                mTestProvider.notifySettingsAvailable(newPolicies);
+                testProvider.refresh();
             }
         });
 
diff --git a/android_webview/test/shell/DEPS b/android_webview/test/shell/DEPS
index 0d019e1..2a76d5bd 100644
--- a/android_webview/test/shell/DEPS
+++ b/android_webview/test/shell/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+components/policy/android/javatests",
   "+content/public/android/java",
 ]
diff --git a/android_webview/test/shell/src/org/chromium/android_webview/test/AwInstrumentationTestRunner.java b/android_webview/test/shell/src/org/chromium/android_webview/test/AwInstrumentationTestRunner.java
new file mode 100644
index 0000000..84f9ceb
--- /dev/null
+++ b/android_webview/test/shell/src/org/chromium/android_webview/test/AwInstrumentationTestRunner.java
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.android_webview.test;
+
+import org.chromium.base.test.BaseInstrumentationTestRunner;
+import org.chromium.base.test.BaseTestResult;
+import org.chromium.policy.test.annotations.Policies;
+
+/**
+ * Instrumentation test runner that allows integrating features defined above base layer
+ * for webview testing.
+ */
+public class AwInstrumentationTestRunner extends BaseInstrumentationTestRunner {
+    @Override
+    protected void addTestHooks(BaseTestResult result) {
+        super.addTestHooks(result);
+
+        result.addPreTestHook(Policies.getRegistrationHook());
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 3fad91e..8b61bca 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -237,7 +237,7 @@
                         // Determine where the library should be loaded from.
                         String zipFilePath = null;
                         String libFilePath = System.mapLibraryName(library);
-                        if (linker.isInZipFile()) {
+                        if (Linker.isInZipFile()) {
                             // Load directly from the APK.
                             zipFilePath = getLibraryApkPath(context);
                             Log.i(TAG, "Loading " + library + " from within " + zipFilePath);
diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc
index 3c7faa32..b766fa6 100644
--- a/base/test/launcher/test_results_tracker.cc
+++ b/base/test/launcher/test_results_tracker.cc
@@ -145,8 +145,7 @@
   // to compare with regular test names, e.g. before or after disabling.
   all_tests_.insert(TestNameWithoutDisabledPrefix(test_name));
 
-  test_locations_.insert(std::make_pair(
-      TestNameWithoutDisabledPrefix(test_name), CodeLocation(file, line)));
+  test_locations_.insert(std::make_pair(test_name, CodeLocation(file, line)));
 }
 
 void TestResultsTracker::AddDisabledTest(const std::string& test_name) {
diff --git a/build/android/play_services/config.json b/build/android/play_services/config.json
index df669e92..f4f9c6e 100644
--- a/build/android/play_services/config.json
+++ b/build/android/play_services/config.json
@@ -1,3 +1,3 @@
 {
-  "version_number": 8115000
+  "version_number": 8298000
 }
diff --git a/build/android/play_services/google_play_services_library.zip.sha1 b/build/android/play_services/google_play_services_library.zip.sha1
index d6178e7b..113d55a 100644
--- a/build/android/play_services/google_play_services_library.zip.sha1
+++ b/build/android/play_services/google_play_services_library.zip.sha1
@@ -1 +1 @@
-d21c06dd47dfa502ce0d9ef9f466ef79a32dc833
\ No newline at end of file
+07308d03b3a83f2985c52e5cfe2764220e19e223
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/data_use_dialog.xml b/chrome/android/java/res/layout/data_use_dialog.xml
index cfc6e17ec..63662c8 100644
--- a/chrome/android/java/res/layout/data_use_dialog.xml
+++ b/chrome/android/java/res/layout/data_use_dialog.xml
@@ -3,16 +3,40 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
-    android:paddingStart="18dp"
-    android:paddingTop="8dp" >
+    android:orientation="vertical"
+    android:paddingBottom="16dp"
+    android:paddingStart="16dp"
+    android:paddingTop="16dp" >
+    
+    <TextView
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_marginBottom="16dp"
+        android:layout_marginStart="8dp"
+        android:text="@string/data_use_tracking_ended_message"
+        android:textColor="@color/light_normal_color"
+        android:textSize="16sp" />
+    
+    <TextView
+        android:id="@+id/learn_more"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent" 
+        android:clickable="true"
+        android:layout_marginBottom="16dp"
+        android:layout_marginStart="8dp"
+        android:text="@string/learn_more"
+        android:textColor="@color/light_active_color"
+        android:textSize="16sp" />
 
     <CheckBox
         android:id="@+id/data_use_checkbox"
         android:layout_height="wrap_content"
-        android:layout_width="match_parent" 
-        android:text="@string/data_use_tracking_ended_checkbox_message" />
+        android:layout_width="match_parent"
+        android:text="@string/data_use_tracking_ended_checkbox_message"
+        android:textColor="@color/light_normal_color"
+        android:textSize="16sp" />
 
-</FrameLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/website_settings.xml b/chrome/android/java/res/layout/website_settings.xml
index 4a7ecd3..9cc5ee339 100644
--- a/chrome/android/java/res/layout/website_settings.xml
+++ b/chrome/android/java/res/layout/website_settings.xml
@@ -20,7 +20,7 @@
         android:paddingEnd="@dimen/website_settings_popup_padding_sides"
         android:paddingStart="@dimen/website_settings_popup_padding_sides" >
 
-        <view class="org.chromium.chrome.browser.WebsiteSettingsPopup$ElidedUrlTextView"
+        <view class="org.chromium.chrome.browser.pageinfo.WebsiteSettingsPopup$ElidedUrlTextView"
             android:id="@+id/website_settings_url"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
index 0e5c2d9..fc158b5e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchFieldTrial.java
@@ -23,8 +23,6 @@
     private static final String ENABLED_PARAM = "enabled";
     private static final String ENABLED_VALUE = "true";
 
-    static final String PROMO_ON_LIMITED_TAPS = "promo_on_limited_taps";
-    static final String TAP_TRIGGERED_PROMO_LIMIT = "tap_triggered_promo_limit";
     static final String PEEK_PROMO_FORCED = "peek_promo_forced";
     static final String PEEK_PROMO_ENABLED = "peek_promo_enabled";
     static final String PEEK_PROMO_MAX_SHOW_COUNT = "peek_promo_max_show_count";
@@ -36,9 +34,6 @@
     @VisibleForTesting
     static final String TRANSLATION_ONEBOX_ENABLED = "translation_onebox_enabled";
 
-    // Tap handling.
-    private static final int UNLIMITED_TAPS = -1;
-
     // Cached values to avoid repeated and redundant JNI operations.
     private static Boolean sEnabled;
     private static Boolean sIsPeekPromoEnabled;
@@ -96,19 +91,6 @@
     }
 
     /**
-     * @return Whether the promo should be triggered by a limited number of taps.
-     */
-    public static boolean isPromoLimitedByTapCounts() {
-        return getBooleanParam(PROMO_ON_LIMITED_TAPS);
-    }
-
-    /**
-     * @return The maximum number of times the promo can be triggered by a tap, or
-     * {@code ContextualSearchUma#PROMO_TAPS_REMAINING_INVALID} if no value is present in the finch
-     * configuration.
-     */
-    static int getPromoTapTriggeredLimit() {
-        return getIntParamValueOrDefault(TAP_TRIGGERED_PROMO_LIMIT, UNLIMITED_TAPS);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
index db237a37..5625c2c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -30,6 +30,7 @@
     private static final Pattern CONTAINS_WHITESPACE_PATTERN = Pattern.compile("\\s");
     private static final int REMAINING_NOT_APPLICABLE = -1;
     private static final int ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+    private static final int TAP_TRIGGERED_PROMO_LIMIT = 50;
     private static final int TAP_RESOLVE_LIMIT_FOR_DECIDED = 50;
     private static final int TAP_PREFETCH_LIMIT_FOR_DECIDED = 50;
     private static final int TAP_RESOLVE_LIMIT_FOR_UNDECIDED = 20;
@@ -43,6 +44,7 @@
     private boolean mDidOverrideDecidedStateForTesting;
     private boolean mDecidedStateForTesting;
     private boolean mDidResetCounters;
+    private Integer mTapTriggeredPromoLimitForTesting;
     private Integer mTapResolveLimitForDecided;
     private Integer mTapPrefetchLimitForDecided;
     private Integer mTapResolveLimitForUndecided;
@@ -78,13 +80,20 @@
         // Return a non-negative value if opt-out promo counter is enabled, and there's a limit.
         DisableablePromoTapCounter counter = getPromoTapCounter();
         if (counter.isEnabled()) {
-            int limit = ContextualSearchFieldTrial.getPromoTapTriggeredLimit();
+            int limit = getPromoTapTriggeredLimit();
             if (limit >= 0) return Math.max(0, limit - counter.getCount());
         }
 
         return REMAINING_NOT_APPLICABLE;
     }
 
+    private int getPromoTapTriggeredLimit() {
+        if (mTapTriggeredPromoLimitForTesting != null) {
+            return mTapTriggeredPromoLimitForTesting.intValue();
+        }
+        return TAP_TRIGGERED_PROMO_LIMIT;
+    }
+
     /**
      * @return the {@link DisableablePromoTapCounter}.
      */
@@ -97,8 +106,7 @@
      */
     boolean isTapSupported() {
         if (!isUserUndecided()) return true;
-        return !ContextualSearchFieldTrial.isPromoLimitedByTapCounts()
-                || getPromoTapsRemaining() != 0;
+        return getPromoTapsRemaining() != 0;
     }
 
     /**
@@ -437,6 +445,14 @@
         return ContextualSearchFieldTrial.isTranslationOneboxEnabled();
     }
 
+    /**
+     * Sets the limit for the tap triggered promo.
+     */
+    @VisibleForTesting
+    void setPromoTapTriggeredLimitForTesting(int limit) {
+        mTapTriggeredPromoLimitForTesting = limit;
+    }
+
     @VisibleForTesting
     void setTapResolveLimitForDecidedForTesting(int limit) {
         mTapResolveLimitForDecided = limit;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datausage/DataUseTabUIManager.java b/chrome/android/java/src/org/chromium/chrome/browser/datausage/DataUseTabUIManager.java
index 035a992..bfe8427 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/datausage/DataUseTabUIManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/datausage/DataUseTabUIManager.java
@@ -15,6 +15,7 @@
 import android.widget.CheckBox;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.EmbedContentViewActivity;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.sessions.SessionTabHelper;
 import org.chromium.chrome.browser.tab.Tab;
@@ -100,12 +101,19 @@
      */
     private static void startDataUseDialog(final Activity activity, final Tab tab,
             final String url, final int pageTransitionType, final String referrerUrl) {
-        View checkBoxView = View.inflate(activity, R.layout.data_use_dialog, null);
-        final CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.data_use_checkbox);
+        View dataUseDialogView = View.inflate(activity, R.layout.data_use_dialog, null);
+        final CheckBox checkBox = (CheckBox) dataUseDialogView.findViewById(R.id.data_use_checkbox);
+        View learnMore = dataUseDialogView.findViewById(R.id.learn_more);
+        learnMore.setOnClickListener(new android.view.View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                EmbedContentViewActivity.show(activity, R.string.data_use_learn_more_title,
+                        R.string.data_use_learn_more_link_url);
+            }
+        });
         new AlertDialog.Builder(activity, R.style.AlertDialogTheme)
                 .setTitle(R.string.data_use_tracking_ended_title)
-                .setMessage(R.string.data_use_tracking_ended_message)
-                .setView(checkBoxView)
+                .setView(dataUseDialogView)
                 .setPositiveButton(R.string.data_use_tracking_ended_continue,
                         new OnClickListener() {
                             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
index b6a71ccc2..f2f50a4f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java
@@ -114,9 +114,6 @@
         }
     }
 
-    private static final int ROW_MAIN = 1;
-    private static final int ROW_OTHER = 2;
-
     private final int mMargin;
     private final int mIconSize;
     private final int mMinWidth;
@@ -414,48 +411,26 @@
         placeChild(mCloseButton, Gravity.END);
         placeGroup(mMainGroup);
 
-        int customGroupWidth = 0;
         if (mCustomGroup != null) {
-            updateCustomGroupForRow(ROW_MAIN);
-            customGroupWidth = getWidthWithMargins(mCustomGroup);
-        }
-
-        int buttonGroupWidth = 0;
-        if (mButtonGroup != null) {
-            updateButtonGroupForRow(ROW_MAIN);
-            buttonGroupWidth = getWidthWithMargins(mButtonGroup);
-        }
-
-        boolean customGroupOnMainRow = customGroupWidth <= availableWidth();
-        boolean buttonGroupOnMainRow = customGroupWidth + buttonGroupWidth <= availableWidth();
-
-        if (mCustomGroup != null) {
-            if (customGroupOnMainRow) {
-                mCustomGroup.gravity = (mButtonGroup != null && buttonGroupOnMainRow)
-                        ? Gravity.START : Gravity.END;
-            } else {
-                startRow();
-                updateCustomGroupForRow(ROW_OTHER);
-            }
+            startRow();
+            updateCustomGroupLayoutProperties();
             placeGroup(mCustomGroup);
         }
 
         if (mButtonGroup != null) {
-            if (!buttonGroupOnMainRow) {
-                startRow();
-                updateButtonGroupForRow(ROW_OTHER);
+            startRow();
+            updateButtonGroupLayoutProperties();
 
-                // If the infobar consists of just a main row and a buttons row, the buttons must be
-                // at least 32dp below the bottom of the message text.
-                if (mCustomGroup == null && mMessageTextView != null) {
-                    LayoutParams lp = (LayoutParams) mMessageTextView.getLayoutParams();
-                    int messageBottom = lp.top + mMessageTextView.getMeasuredHeight();
-                    mTop = Math.max(mTop, messageBottom + 2 * mMargin);
-                }
+            // If the infobar consists of just a main row and a buttons row, the buttons must be
+            // at least 32dp below the bottom of the message text.
+            if (mCustomGroup == null && mMessageTextView != null) {
+                LayoutParams lp = (LayoutParams) mMessageTextView.getLayoutParams();
+                int messageBottom = lp.top + mMessageTextView.getMeasuredHeight();
+                mTop = Math.max(mTop, messageBottom + 2 * mMargin);
             }
             placeGroup(mButtonGroup);
 
-            if (mCustomButton != null && !buttonGroupOnMainRow) {
+            if (mCustomButton != null) {
                 // The custom button is start-aligned with the message view (unless that causes it
                 // to overlap the primary button).
                 LayoutParams primaryButtonLP = (LayoutParams) mPrimaryButton.getLayoutParams();
@@ -476,17 +451,6 @@
         }
 
         startRow();
-
-        // If everything fits on a single row, center everything vertically.
-        if (buttonGroupOnMainRow) {
-            int layoutHeight = mBottom;
-            for (int i = 0; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                int extraSpace = layoutHeight - child.getMeasuredHeight();
-                LayoutParams lp = (LayoutParams) child.getLayoutParams();
-                lp.top = extraSpace / 2;
-            }
-        }
     }
 
     /**
@@ -572,18 +536,15 @@
     }
 
     /**
-     * The button group has different layout properties (margins, gravity, etc) when placed on the
-     * main row as opposed to on a separate row. This updates the layout properties of the button
-     * group to prepare for placing it on either the main row or a separate row.
-     *
-     * @param row One of ROW_MAIN or ROW_OTHER.
+     * Updates the layout properties (margins, gravity, etc) of the button group to prepare for
+     * placing it on its own row.
      */
-    private void updateButtonGroupForRow(int row) {
-        int startEndMargin = row == ROW_MAIN ? mMargin : 0;
+    private void updateButtonGroupLayoutProperties() {
+        int startEndMargin = 0;
         mButtonGroup.setHorizontalMode(mMargin / 2, startEndMargin, startEndMargin);
         mButtonGroup.gravity = Gravity.END;
 
-        if (row == ROW_OTHER && mButtonGroup.views.length >= 2) {
+        if (mButtonGroup.views.length >= 2) {
             int extraWidth = availableWidth() - getWidthWithMargins(mButtonGroup);
             if (extraWidth < 0) {
                 // Group is too wide to fit on a single row, so stack the group items vertically.
@@ -594,21 +555,18 @@
                 ((LayoutParams) mTertiaryButton.getLayoutParams()).endMargin += extraWidth;
             }
         }
-        if (row == ROW_MAIN && mCustomButton != null) {
-            // Increase spacing between custom button and primary button.
-            ((LayoutParams) mCustomButton.getLayoutParams()).endMargin = mMargin;
-        }
     }
 
     /**
-     * Analagous to updateButtonGroupForRow(), but for the custom group istead of the button group.
+     * Analagous to updateButtonGroupLayoutProperties(), but for the custom group instead of the
+     * button group.
      */
-    private void updateCustomGroupForRow(int row) {
-        int startEndMargin = row == ROW_MAIN ? mMargin : 0;
+    private void updateCustomGroupLayoutProperties() {
+        int startEndMargin = 0;
         mCustomGroup.setHorizontalMode(mMargin, startEndMargin, startEndMargin);
         mCustomGroup.gravity = Gravity.START;
 
-        if (row == ROW_OTHER && mCustomGroup.views.length == 2) {
+        if (mCustomGroup.views.length == 2) {
             int extraWidth = availableWidth() - getWidthWithMargins(mCustomGroup);
             if (extraWidth < 0) {
                 // Group is too wide to fit on a single row, so stack the group items vertically.
@@ -624,7 +582,7 @@
                 measureChildWithFixedWidth(view1, view1.getMeasuredWidth() + extraWidth1);
             }
         }
-        if (row == ROW_OTHER && mCustomGroup.views.length == 1) {
+        if (mCustomGroup.views.length == 1) {
             mCustomGroup.gravity = Gravity.FILL_HORIZONTAL;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/notifications/OWNERS
index a1a35ba4..2fc004d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/OWNERS
@@ -1,2 +1,3 @@
 miguelg@chromium.org
+mvanouwerkerk@chromium.org
 peter@chromium.org
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataUseSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataUseSnackbarController.java
index adacddf..eb295d86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataUseSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataUseSnackbarController.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.EmbedContentViewActivity;
 
 /**
  * The controller for two data use snackbars:
@@ -34,14 +35,16 @@
     public void showDataUseTrackingStartedBar() {
         mSnackbarManager.showSnackbar(Snackbar.make(
                 mContext.getString(R.string.data_use_tracking_started_snackbar_message), this)
-                .setAction(mContext.getString(R.string.data_use_tracking_started_snackbar_action),
+                .setAction(mContext.getString(R.string.data_use_tracking_snackbar_action),
                         null));
         // TODO(megjablon): Add metrics.
     }
 
     public void showDataUseTrackingEndedBar() {
         mSnackbarManager.showSnackbar(Snackbar.make(
-                mContext.getString(R.string.data_use_tracking_ended_title), this));
+                mContext.getString(R.string.data_use_tracking_ended_snackbar_message), this)
+                .setAction(mContext.getString(R.string.data_use_tracking_snackbar_action),
+                        null));
         // TODO(megjablon): Add metrics.
     }
 
@@ -57,7 +60,9 @@
      */
     @Override
     public void onAction(Object actionData) {
-        // TODO(megjablon): Load the more page and add metrics.
+        // TODO(megjablon): Add metrics.
+        EmbedContentViewActivity.show(mContext, R.string.data_use_learn_more_title,
+                R.string.data_use_learn_more_link_url);
     }
 
     @Override
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 2bd8e571..bf7939b7 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -778,23 +778,32 @@
 
       <!-- Data Use -->
       <message name="IDS_DATA_USE_TRACKING_STARTED_SNACKBAR_MESSAGE" desc="Message shown when data use tracking starts.">
-         Data use is being tracked
+        Data use is being tracked
       </message>
-      <message name="IDS_DATA_USE_TRACKING_STARTED_SNACKBAR_ACTION" desc="Button to learn more when data use tracking starts.">
+      <message name="IDS_DATA_USE_TRACKING_SNACKBAR_ACTION" desc="Button to learn more when data use tracking starts or ends.">
         More
       </message>
-      <message name="IDS_DATA_USE_TRACKING_ENDED_TITLE" desc="Message shown when data use tracking has ended. Used by both the snackbar and dialog.">
+      <message name="IDS_DATA_USE_TRACKING_ENDED_SNACKBAR_MESSAGE" desc="Message shown when data use tracking has ended.">
         Data use tracking has ended
       </message>
+      <message name="IDS_DATA_USE_TRACKING_ENDED_TITLE" desc="Message shown on the dialog when data use tracking has ended.">
+        Data use tracking ended
+      </message>
       <message name="IDS_DATA_USE_TRACKING_ENDED_MESSAGE" desc="Message shown on the dialog when data use tracking has ended.">
         Your data use will no longer be measured.
       </message>
       <message name="IDS_DATA_USE_TRACKING_ENDED_CHECKBOX_MESSAGE" desc="Message shown to opt out of seeing dialogs when data use tracking has ended.">
-        Don’t ask me again
+        Don’t show this again
       </message>
       <message name="IDS_DATA_USE_TRACKING_ENDED_CONTINUE" desc="Continue button for the dialog shown when data use tracking has ended. When clicked, the page resumes loading.">
         Continue
       </message>
+      <message name="IDS_DATA_USE_LEARN_MORE_TITLE" desc="Title for the 'Learn more' link in the the dialog shown when data use tracking has ended">
+        Learn more
+      </message>
+      <message name="IDS_DATA_USE_LEARN_MORE_LINK_URL" desc="URL for the 'Learn more' link in the the dialog shown when data use tracking has ended">
+        <!-- Intentionally empty. This is overridden downstream. -->
+      </message>
 
       <!-- About Chrome preferences -->
       <message name="IDS_PREFS_ABOUT_CHROME" desc="Title for the About Chrome page. [CHAR-LIMIT=32]">
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index 5f0b8bc..ea6d261 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -1616,12 +1616,10 @@
      * @Feature({"ContextualSearch"})
      */
     @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-    @CommandLineFlags.Add({
-            ContextualSearchFieldTrial.PROMO_ON_LIMITED_TAPS + "=true",
-            ContextualSearchFieldTrial.TAP_TRIGGERED_PROMO_LIMIT + "=2"})
     @FlakyTest
     public void testTapTriggeredPromoLimitForOptOut()
             throws InterruptedException, TimeoutException {
+        mPolicy.setPromoTapTriggeredLimitForTesting(2);
         mPolicy.overrideDecidedStateForTesting(false);
 
         clickWordNode("states");
@@ -1863,11 +1861,9 @@
      * @Feature({"ContextualSearch"})
      */
     @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
-    @CommandLineFlags.Add({
-            ContextualSearchFieldTrial.PROMO_ON_LIMITED_TAPS + "=true",
-            ContextualSearchFieldTrial.TAP_TRIGGERED_PROMO_LIMIT + "=2"})
     @FlakyTest
     public void testPromoTapCount() throws InterruptedException, TimeoutException {
+        mPolicy.setPromoTapTriggeredLimitForTesting(2);
         // Note that this tests the basic underlying counter used by
         // testTapTriggeredPromoLimitForOptOut.
         // TODO(donnd): consider removing either this test or testTapTriggeredPromoLimitForOptOut.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/invalidations/InvalidationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/invalidations/InvalidationControllerTest.java
index 2892093..93fdbbf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/invalidations/InvalidationControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/invalidations/InvalidationControllerTest.java
@@ -22,6 +22,7 @@
 import org.chromium.sync.notifier.InvalidationIntentProtocol;
 import org.chromium.sync.signin.ChromeSigninController;
 import org.chromium.sync.test.util.MockSyncContentResolverDelegate;
+import org.chromium.testing.local.CustomShadowAsyncTask;
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.junit.Assert;
 import org.junit.Before;
@@ -39,7 +40,7 @@
  * Tests for the {@link InvalidationController}.
  */
 @RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
+@Config(manifest = Config.NONE, shadows = {CustomShadowAsyncTask.class})
 public class InvalidationControllerTest {
     /**
      * Stubbed out ProfileSyncService with a setter to control return value of
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index ad570f1..09407f9db 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -246,9 +246,6 @@
 #define IDC_TRANSLATE_ORIGINAL_LANGUAGE_BASE 42100
 #define IDC_TRANSLATE_TARGET_LANGUAGE_BASE   42400
 
-// Speech input
-#define IDC_TOGGLE_SPEECH_INPUT         42500
-
 // Identifiers for platform-specific items.
 // Placed in a common file to help insure they never collide.
 #define IDC_VIEW_MENU                   44000     // OSX only
diff --git a/chrome/app/chrome_dll.rc b/chrome/app/chrome_dll.rc
index c132fea..4dc8a15 100644
--- a/chrome/app/chrome_dll.rc
+++ b/chrome/app/chrome_dll.rc
@@ -115,7 +115,6 @@
     "M",            IDC_SHOW_AVATAR_MENU,       VIRTKEY, CONTROL, SHIFT
     VK_ESCAPE,      IDC_STOP,                   VIRTKEY
     VK_ESCAPE,      IDC_TASK_MANAGER,           VIRTKEY, SHIFT
-    VK_OEM_PERIOD,  IDC_TOGGLE_SPEECH_INPUT,    VIRTKEY, CONTROL, SHIFT
     "U",            IDC_VIEW_SOURCE,            VIRTKEY, CONTROL
     VK_OEM_MINUS,   IDC_ZOOM_MINUS,             VIRTKEY, CONTROL
     VK_OEM_MINUS,   IDC_ZOOM_MINUS,             VIRTKEY, CONTROL, SHIFT
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 7108217..54a8f27 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -23,17 +23,11 @@
     <message name="IDS_SETTINGS_HIGH_CONTRAST_LABEL" desc="Label for checkbox which enables high-contrast UI.">
       Use high contrast mode
     </message>
-    <message name="IDS_SETTINGS_STICKY_KEYS_LABEL" desc="Label for checkbox which enables sticky keys.">
-      Enable sticky keys
+    <message name="IDS_SETTINGS_STICKY_KEYS_LABEL" desc="Label for checkbox which enables sticky keys, with an explanation of the term 'sticky keys'.">
+      Enable sticky keys (to perform keyboard shortcuts by typing them sequentially)
     </message>
-    <message name="IDS_SETTINGS_STICKY_KEYS_SUBLABEL" desc="Sub-label describing what the term 'sticky keys' means.">
-      (to perform keyboard shortcuts by typing them sequentially)
-    </message>
-    <message name="IDS_SETTINGS_CHROMEVOX_LABEL" desc="Label for checkbox which enables ChromeVox">
-      Enable ChromeVox
-    </message>
-    <message name="IDS_SETTINGS_CHROMEVOX_SUBLABEL" desc="Sub-label describingn what ChromeVox is.">
-      (spoken feedback)
+    <message name="IDS_SETTINGS_CHROMEVOX_LABEL" desc="Label for checkbox which enables ChromeVox, with a description of what ChromeVox is.">
+      Enable ChromeVox (spoken feedback)
     </message>
     <message name="IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL" desc="Label for checkbox which enables the screen magnifier">
       Enable screen magnifier
diff --git a/chrome/app/theme/default_100_percent/common/omnibox_mic_search.png b/chrome/app/theme/default_100_percent/common/omnibox_mic_search.png
deleted file mode 100644
index 90b98f1..0000000
--- a/chrome/app/theme/default_100_percent/common/omnibox_mic_search.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/omnibox_mic_search.png b/chrome/app/theme/default_200_percent/common/omnibox_mic_search.png
deleted file mode 100644
index 31ea0fdb..0000000
--- a/chrome/app/theme/default_200_percent/common/omnibox_mic_search.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/material_100_percent/common/omnibox_mic_search.png b/chrome/app/theme/material_100_percent/common/omnibox_mic_search.png
deleted file mode 100644
index 8fb133a0..0000000
--- a/chrome/app/theme/material_100_percent/common/omnibox_mic_search.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/material_200_percent/common/omnibox_mic_search.png b/chrome/app/theme/material_200_percent/common/omnibox_mic_search.png
deleted file mode 100644
index 4541c7d..0000000
--- a/chrome/app/theme/material_200_percent/common/omnibox_mic_search.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 2d9d1239..c39d828 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -516,7 +516,6 @@
       <if expr="not is_macosx and not is_ios">
         <structure type="chrome_scaled_image" name="IDR_OMNIBOX_KEYWORD_HINT_TAB" file="common/omnibox_keyword_hint_tab.png" />
       </if>
-      <structure type="chrome_scaled_image" name="IDR_OMNIBOX_MIC_SEARCH" file="common/omnibox_mic_search.png" />
       <structure type="chrome_scaled_image" name="IDR_OMNIBOX_PDF_ICON" file="pdf.png" />
       <structure type="chrome_scaled_image" name="IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM" file="common/omnibox_popup_border_and_shadow_bottom.png" />
       <structure type="chrome_scaled_image" name="IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW_BOTTOM_LEFT" file="common/omnibox_popup_border_and_shadow_bottom_left.png" />
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index d9bd18d..aa2d3ee 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -106,8 +106,6 @@
     {false, false, false, false, kVK_Delete,       0,   IDC_BACK},
     {false, true,  false, false, kVK_Delete,       0,   IDC_FORWARD},
     {true,  true,  false, false, 0,                'c', IDC_DEV_TOOLS_INSPECT},
-    {true,  true,  false, false, kVK_ANSI_Period,  0,
-     IDC_TOGGLE_SPEECH_INPUT},
   };
 
   *num_entries = arraysize(keyboard_shortcuts);
diff --git a/chrome/browser/media/android/router/media_router_android.cc b/chrome/browser/media/android/router/media_router_android.cc
index 614d4d9..02fcfb78 100644
--- a/chrome/browser/media/android/router/media_router_android.cc
+++ b/chrome/browser/media/android/router/media_router_android.cc
@@ -312,6 +312,16 @@
   NOTIMPLEMENTED();
 }
 
+void MediaRouterAndroid::RegisterPresentationConnectionStateObserver(
+    PresentationConnectionStateObserver* observer) {
+  NOTIMPLEMENTED();
+}
+
+void MediaRouterAndroid::UnregisterPresentationConnectionStateObserver(
+    PresentationConnectionStateObserver* observer) {
+  NOTIMPLEMENTED();
+}
+
 void MediaRouterAndroid::OnSinksReceived(
     JNIEnv* env,
     jobject obj,
diff --git a/chrome/browser/media/android/router/media_router_android.h b/chrome/browser/media/android/router/media_router_android.h
index b7d481b3..610a22b 100644
--- a/chrome/browser/media/android/router/media_router_android.h
+++ b/chrome/browser/media/android/router/media_router_android.h
@@ -107,6 +107,10 @@
       LocalMediaRoutesObserver* observer) override;
   void UnregisterLocalMediaRoutesObserver(
       LocalMediaRoutesObserver* observer) override;
+  void RegisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) override;
+  void UnregisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) override;
 
   base::android::ScopedJavaGlobalRef<jobject> java_media_router_;
 
diff --git a/chrome/browser/media/router/media_router.gypi b/chrome/browser/media/router/media_router.gypi
index aed29aa..32089b1 100644
--- a/chrome/browser/media/router/media_router.gypi
+++ b/chrome/browser/media/router/media_router.gypi
@@ -35,6 +35,8 @@
       'media_source.h',
       'media_source_helper.cc',
       'media_source_helper.h',
+      'presentation_connection_state_observer.cc',
+      'presentation_connection_state_observer.h',
       'presentation_media_sinks_observer.cc',
       'presentation_media_sinks_observer.h',
       'presentation_service_delegate_impl.cc',
diff --git a/chrome/browser/media/router/media_router.h b/chrome/browser/media/router/media_router.h
index f355c58..5836a9c3 100644
--- a/chrome/browser/media/router/media_router.h
+++ b/chrome/browser/media/router/media_router.h
@@ -27,6 +27,7 @@
 class LocalMediaRoutesObserver;
 class MediaRoutesObserver;
 class MediaSinksObserver;
+class PresentationConnectionStateObserver;
 class PresentationSessionMessagesObserver;
 
 // Type of callback used in |CreateRoute()| and |JoinRoute()|. Callback is
@@ -125,6 +126,7 @@
   friend class LocalMediaRoutesObserver;
   friend class MediaSinksObserver;
   friend class MediaRoutesObserver;
+  friend class PresentationConnectionStateObserver;
   friend class PresentationSessionMessagesObserver;
 
   // The following functions are called by friend Observer classes above.
@@ -189,6 +191,15 @@
   // Removes the LocalMediaRoutesObserver |observer|.
   virtual void UnregisterLocalMediaRoutesObserver(
       LocalMediaRoutesObserver* observer) = 0;
+
+  // Registers/unregisters a PresentationConnectionStateObserver to receive
+  // updates on state changes for a PresentationConnection. MediaRouter does
+  // not own |observer|. When |observer| is about to be destroyed, it must be
+  // unregistered from MediaRouter.
+  virtual void RegisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) = 0;
+  virtual void UnregisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) = 0;
 };
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/media_router.mojom b/chrome/browser/media/router/media_router.mojom
index 2330a1e9..6289a2d 100644
--- a/chrome/browser/media/router/media_router.mojom
+++ b/chrome/browser/media/router/media_router.mojom
@@ -201,6 +201,13 @@
     AVAILABLE
   };
 
+  // Keep in sync with content/public/browser/presentation_session.h.
+  enum PresentationConnectionState {
+    CONNECTED,
+    CLOSED,
+    TERMINATED
+  };
+
   // Registers a MediaRouteProvider with the MediaRouter.
   // Returns a string that uniquely identifies the Media Router browser
   // process.
@@ -218,5 +225,10 @@
 
   // Called when the overall availability of media sinks has been updated.
   OnSinkAvailabilityUpdated(SinkAvailability availability);
+
+  // Called when the state of presentation connected to route |route_id| has
+  // changed to |state|.
+  OnPresentationConnectionStateChanged(
+      string route_id, PresentationConnectionState state);
 };
 
diff --git a/chrome/browser/media/router/media_router_mojo_impl.cc b/chrome/browser/media/router/media_router_mojo_impl.cc
index d6bb508..e7bc9d3 100644
--- a/chrome/browser/media/router/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/media_router_mojo_impl.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/media/router/media_router_type_converters.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
 #include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/presentation_connection_state_observer.h"
 #include "chrome/browser/media/router/presentation_session_messages_observer.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "extensions/browser/process_manager.h"
@@ -499,6 +500,40 @@
   local_routes_observers_.RemoveObserver(observer);
 }
 
+void MediaRouterMojoImpl::RegisterPresentationConnectionStateObserver(
+    PresentationConnectionStateObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(observer);
+
+  const MediaRoute::Id route_id = observer->route_id();
+  auto* observers = presentation_connection_state_observers_.get(route_id);
+  if (!observers) {
+    observers = new PresentationConnectionStateObserverList;
+    presentation_connection_state_observers_.add(route_id,
+                                                 make_scoped_ptr(observers));
+  }
+
+  if (observers->HasObserver(observer))
+    return;
+
+  observers->AddObserver(observer);
+}
+
+void MediaRouterMojoImpl::UnregisterPresentationConnectionStateObserver(
+    PresentationConnectionStateObserver* observer) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(observer);
+
+  const MediaRoute::Id route_id = observer->route_id();
+  auto* observers = presentation_connection_state_observers_.get(route_id);
+  if (!observers)
+    return;
+
+  observers->RemoveObserver(observer);
+  if (!observers->might_have_observers())
+    presentation_connection_state_observers_.erase(route_id);
+}
+
 void MediaRouterMojoImpl::DoCreateRoute(
     const MediaSource::Id& source_id,
     const MediaSink::Id& sink_id,
@@ -644,6 +679,25 @@
   }
 }
 
+void MediaRouterMojoImpl::OnPresentationConnectionStateChanged(
+    const mojo::String& route_id,
+    interfaces::MediaRouter::PresentationConnectionState state) {
+  if (!interfaces::MediaRouter::PresentationConnectionState_IsValidValue(
+          state)) {
+    DLOG(WARNING) << "Unknown PresentationConnectionState value " << state;
+    return;
+  }
+
+  auto* observers = presentation_connection_state_observers_.get(route_id);
+  if (!observers)
+    return;
+
+  content::PresentationConnectionState converted_state =
+      mojo::PresentationConnectionStateFromMojo(state);
+  FOR_EACH_OBSERVER(PresentationConnectionStateObserver, *observers,
+                    OnStateChanged(converted_state));
+}
+
 void MediaRouterMojoImpl::DoOnPresentationSessionDetached(
     const MediaRoute::Id& route_id) {
   DVLOG_WITH_INSTANCE(1) << "DoOnPresentationSessionDetached " << route_id;
diff --git a/chrome/browser/media/router/media_router_mojo_impl.h b/chrome/browser/media/router/media_router_mojo_impl.h
index 054223d..f2ce7c5 100644
--- a/chrome/browser/media/router/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/media_router_mojo_impl.h
@@ -199,6 +199,10 @@
       LocalMediaRoutesObserver* observer) override;
   void UnregisterLocalMediaRoutesObserver(
       LocalMediaRoutesObserver* observer) override;
+  void RegisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) override;
+  void UnregisterPresentationConnectionStateObserver(
+      PresentationConnectionStateObserver* observer) override;
 
   // These calls invoke methods in the component extension via Mojo.
   void DoCreateRoute(const MediaSource::Id& source_id,
@@ -249,6 +253,9 @@
   void OnRoutesUpdated(mojo::Array<interfaces::MediaRoutePtr> routes) override;
   void OnSinkAvailabilityUpdated(
       interfaces::MediaRouter::SinkAvailability availability) override;
+  void OnPresentationConnectionStateChanged(
+      const mojo::String& route_id,
+      interfaces::MediaRouter::PresentationConnectionState state) override;
 
   // Converts the callback result of calling Mojo CreateRoute()/JoinRoute()
   // into a local callback.
@@ -292,12 +299,19 @@
   base::ScopedPtrHashMap<MediaRoute::Id,
                          scoped_ptr<PresentationSessionMessagesObserverList>>
       messages_observers_;
+
   // IDs of MediaRoutes being listened for messages. Note that this is
   // different from |message_observers_| because we might be waiting for
   // |OnRouteMessagesReceived()| to be invoked after all observers for that
   // route have been removed.
   std::set<MediaRoute::Id> route_ids_listening_for_messages_;
 
+  using PresentationConnectionStateObserverList =
+      base::ObserverList<PresentationConnectionStateObserver>;
+  base::ScopedPtrHashMap<MediaRoute::Id,
+                         scoped_ptr<PresentationConnectionStateObserverList>>
+      presentation_connection_state_observers_;
+
   IssueManager issue_manager_;
 
   // Binds |this| to a Mojo connection stub for interfaces::MediaRouter.
diff --git a/chrome/browser/media/router/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
index be57f699..bb7b38f 100644
--- a/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/media_router_mojo_impl_unittest.cc
@@ -733,6 +733,28 @@
   ProcessEventLoop();
 }
 
+TEST_F(MediaRouterMojoImplTest, PresentationConnectionStateObserver) {
+  using PresentationConnectionState =
+      interfaces::MediaRouter::PresentationConnectionState;
+
+  MediaRoute::Id route_id("route-id");
+  MockPresentationConnectionStateObserver observer(router(), route_id);
+
+  EXPECT_CALL(observer,
+              OnStateChanged(content::PRESENTATION_CONNECTION_STATE_CLOSED));
+  media_router_proxy_->OnPresentationConnectionStateChanged(
+      route_id,
+      PresentationConnectionState::PRESENTATION_CONNECTION_STATE_CLOSED);
+  ProcessEventLoop();
+
+  EXPECT_CALL(observer, OnStateChanged(
+                            content::PRESENTATION_CONNECTION_STATE_TERMINATED));
+  media_router_proxy_->OnPresentationConnectionStateChanged(
+      route_id,
+      PresentationConnectionState::PRESENTATION_CONNECTION_STATE_TERMINATED);
+  ProcessEventLoop();
+}
+
 TEST_F(MediaRouterMojoImplTest, QueuedWhileAsleep) {
   EXPECT_CALL(mock_event_page_tracker_, IsEventPageSuspended(extension_id()))
       .Times(2)
diff --git a/chrome/browser/media/router/media_router_type_converters.cc b/chrome/browser/media/router/media_router_type_converters.cc
index 493b613..627bf9de 100644
--- a/chrome/browser/media/router/media_router_type_converters.cc
+++ b/chrome/browser/media/router/media_router_type_converters.cc
@@ -5,8 +5,11 @@
 #include "chrome/browser/media/router/media_router_type_converters.h"
 
 using media_router::interfaces::IssuePtr;
-using media_router::interfaces::MediaSinkPtr;
 using media_router::interfaces::MediaRoutePtr;
+using media_router::interfaces::MediaSinkPtr;
+
+using PresentationConnectionState =
+    media_router::interfaces::MediaRouter::PresentationConnectionState;
 
 namespace mojo {
 
@@ -126,4 +129,19 @@
       input->is_blocking, input->help_url);
 }
 
+content::PresentationConnectionState PresentationConnectionStateFromMojo(
+    PresentationConnectionState state) {
+  switch (state) {
+    case PresentationConnectionState::PRESENTATION_CONNECTION_STATE_CONNECTED:
+      return content::PRESENTATION_CONNECTION_STATE_CONNECTED;
+    case PresentationConnectionState::PRESENTATION_CONNECTION_STATE_CLOSED:
+      return content::PRESENTATION_CONNECTION_STATE_CLOSED;
+    case PresentationConnectionState::PRESENTATION_CONNECTION_STATE_TERMINATED:
+      return content::PRESENTATION_CONNECTION_STATE_TERMINATED;
+    default:
+      NOTREACHED() << "Unknown PresentationConnectionState " << state;
+      return content::PRESENTATION_CONNECTION_STATE_TERMINATED;
+  }
+}
+
 }  // namespace mojo
diff --git a/chrome/browser/media/router/media_router_type_converters.h b/chrome/browser/media/router/media_router_type_converters.h
index 5ef4cbe..8dc5dbe 100644
--- a/chrome/browser/media/router/media_router_type_converters.h
+++ b/chrome/browser/media/router/media_router_type_converters.h
@@ -13,6 +13,7 @@
 #include "chrome/browser/media/router/media_router.mojom.h"
 #include "chrome/browser/media/router/media_sink.h"
 #include "chrome/browser/media/router/media_source.h"
+#include "content/public/browser/presentation_session.h"
 #include "mojo/common/common_type_converters.h"
 
 namespace mojo {
@@ -62,6 +63,10 @@
       const media_router::interfaces::IssuePtr& input);
 };
 
+// PresentationConnectionState conversion.
+content::PresentationConnectionState PresentationConnectionStateFromMojo(
+    media_router::interfaces::MediaRouter::PresentationConnectionState state);
+
 }  // namespace mojo
 
 #endif  // CHROME_BROWSER_MEDIA_ROUTER_MEDIA_ROUTER_TYPE_CONVERTERS_H_
diff --git a/chrome/browser/media/router/mock_media_router.h b/chrome/browser/media/router/mock_media_router.h
index 37738192..01fa7b5 100644
--- a/chrome/browser/media/router/mock_media_router.h
+++ b/chrome/browser/media/router/mock_media_router.h
@@ -72,6 +72,10 @@
                void(LocalMediaRoutesObserver* observer));
   MOCK_METHOD1(UnregisterLocalMediaRoutesObserver,
                void(LocalMediaRoutesObserver* observer));
+  MOCK_METHOD1(RegisterPresentationConnectionStateObserver,
+               void(PresentationConnectionStateObserver* observer));
+  MOCK_METHOD1(UnregisterPresentationConnectionStateObserver,
+               void(PresentationConnectionStateObserver* observer));
 };
 
 }  // namespace media_router
diff --git a/chrome/browser/media/router/presentation_connection_state_observer.cc b/chrome/browser/media/router/presentation_connection_state_observer.cc
new file mode 100644
index 0000000..47f342c
--- /dev/null
+++ b/chrome/browser/media/router/presentation_connection_state_observer.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/media/router/presentation_connection_state_observer.h"
+
+#include "base/logging.h"
+#include "chrome/browser/media/router/media_router.h"
+
+namespace media_router {
+
+PresentationConnectionStateObserver::PresentationConnectionStateObserver(
+    MediaRouter* router,
+    const MediaRoute::Id& route_id)
+    : router_(router), route_id_(route_id) {
+  DCHECK(router_);
+  router_->RegisterPresentationConnectionStateObserver(this);
+}
+
+PresentationConnectionStateObserver::~PresentationConnectionStateObserver() {
+  router_->UnregisterPresentationConnectionStateObserver(this);
+}
+
+}  // namespace media_router
diff --git a/chrome/browser/media/router/presentation_connection_state_observer.h b/chrome/browser/media/router/presentation_connection_state_observer.h
new file mode 100644
index 0000000..555f9fe
--- /dev/null
+++ b/chrome/browser/media/router/presentation_connection_state_observer.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_CONNECTION_STATE_OBSERVER_H_
+#define CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_CONNECTION_STATE_OBSERVER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/media/router/media_route.h"
+
+namespace media_router {
+
+class MediaRouter;
+
+// Base class for observing state changes on a PresentationConnection.
+class PresentationConnectionStateObserver {
+ public:
+  // |router|: The MediaRouter instance to register this instance with.
+  // |route_id|: ID of MediaRoute connected to the PresentationConnection to
+  // be observed.
+  PresentationConnectionStateObserver(MediaRouter* router,
+                                      const MediaRoute::Id& route_id);
+  virtual ~PresentationConnectionStateObserver();
+
+  MediaRoute::Id route_id() const { return route_id_; }
+
+  // Invoked when the state of PresentationConnection represented by |route_id_|
+  // has changed to |state|.
+  virtual void OnStateChanged(content::PresentationConnectionState state) {}
+
+ private:
+  MediaRouter* const router_;
+  const MediaRoute::Id route_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(PresentationConnectionStateObserver);
+};
+
+}  // namespace media_router
+
+#endif  // CHROME_BROWSER_MEDIA_ROUTER_PRESENTATION_CONNECTION_STATE_OBSERVER_H_
diff --git a/chrome/browser/media/router/test_helper.cc b/chrome/browser/media/router/test_helper.cc
index e8d4807..1bba86fa 100644
--- a/chrome/browser/media/router/test_helper.cc
+++ b/chrome/browser/media/router/test_helper.cc
@@ -28,4 +28,12 @@
 MockEventPageTracker::~MockEventPageTracker() {
 }
 
+MockPresentationConnectionStateObserver::
+    MockPresentationConnectionStateObserver(MediaRouter* router,
+                                            const MediaRoute::Id& route_id)
+    : PresentationConnectionStateObserver(router, route_id) {}
+
+MockPresentationConnectionStateObserver::
+    ~MockPresentationConnectionStateObserver() = default;
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/test_helper.h b/chrome/browser/media/router/test_helper.h
index 1c5bf8d..24af92df 100644
--- a/chrome/browser/media/router/test_helper.h
+++ b/chrome/browser/media/router/test_helper.h
@@ -13,6 +13,7 @@
 #include "chrome/browser/media/router/media_router_mojo_impl.h"
 #include "chrome/browser/media/router/media_routes_observer.h"
 #include "chrome/browser/media/router/media_sinks_observer.h"
+#include "chrome/browser/media/router/presentation_connection_state_observer.h"
 #include "extensions/browser/event_page_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -139,11 +140,22 @@
 class MockMediaRoutesObserver : public MediaRoutesObserver {
  public:
   explicit MockMediaRoutesObserver(MediaRouter* router);
-  ~MockMediaRoutesObserver();
+  ~MockMediaRoutesObserver() override;
 
   MOCK_METHOD1(OnRoutesUpdated, void(const std::vector<MediaRoute>& sinks));
 };
 
+class MockPresentationConnectionStateObserver
+    : public PresentationConnectionStateObserver {
+ public:
+  MockPresentationConnectionStateObserver(MediaRouter* router,
+                                          const MediaRoute::Id& route_id);
+  ~MockPresentationConnectionStateObserver() override;
+
+  MOCK_METHOD1(OnStateChanged,
+               void(content::PresentationConnectionState state));
+};
+
 class MockEventPageTracker : public extensions::EventPageTracker {
  public:
   MockEventPageTracker();
diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc
index 31a095e0..55fde20 100644
--- a/chrome/browser/memory/tab_manager.cc
+++ b/chrome/browser/memory/tab_manager.cc
@@ -440,6 +440,15 @@
 
   WebContents* web_contents = model->GetWebContentsAt(idx);
 
+  // Do not discard tabs that don't have a valid URL (most probably they have
+  // just been opened and dicarding them would lose the URL).
+  // TODO(georgesak): Look into a workaround to be able to kill the tab without
+  // losing the pending navigation.
+  if (!web_contents->GetLastCommittedURL().is_valid() ||
+      web_contents->GetLastCommittedURL().is_empty()) {
+    return false;
+  }
+
   // Do not discard tabs in which the user has entered text in a form, lest that
   // state gets lost.
   if (web_contents->GetPageImportanceSignals().had_form_interaction)
diff --git a/chrome/browser/memory/tab_manager_browsertest.cc b/chrome/browser/memory/tab_manager_browsertest.cc
index e28ddde..9a13eaa 100644
--- a/chrome/browser/memory/tab_manager_browsertest.cc
+++ b/chrome/browser/memory/tab_manager_browsertest.cc
@@ -216,6 +216,37 @@
   EXPECT_TRUE(tab_manager->recent_tab_discard());
 }
 
+IN_PROC_BROWSER_TEST_F(TabManagerTest, InvalidOrEmptyURL) {
+  TabManager* tab_manager = g_browser_process->GetTabManager();
+  ASSERT_TRUE(tab_manager);
+
+  // Open two tabs. Wait for the foreground one to load but do not wait for the
+  // background one.
+  content::WindowedNotificationObserver load1(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  OpenURLParams open1(GURL(chrome::kChromeUIAboutURL), content::Referrer(),
+                      CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false);
+  browser()->OpenURL(open1);
+  load1.Wait();
+
+  content::WindowedNotificationObserver load2(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  OpenURLParams open2(GURL(chrome::kChromeUICreditsURL), content::Referrer(),
+                      NEW_BACKGROUND_TAB, ui::PAGE_TRANSITION_TYPED, false);
+  browser()->OpenURL(open2);
+
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+
+  // This shouldn't be able to discard a tab as the background tab has not yet
+  // started loading (its URL is not committed).
+  EXPECT_FALSE(tab_manager->DiscardTab());
+
+  // Wait for the background tab to load which then allows it to be discarded.
+  load2.Wait();
+  EXPECT_TRUE(tab_manager->DiscardTab());
+}
 }  // namespace memory
 
 #endif  // OS_WIN || OS_CHROMEOS
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 105a1db..c3bce7e 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -31,10 +31,11 @@
   ]
   if (chromevox_compress_js) {
     deps += [
-      ":chromevox1_content_script",
-      ":chromevox1_kbexplorer_script",
-      ":chromevox1_options_script",
-      ":chromevox2_background_script",
+      ":chromevox_content_script",
+      ":chromevox_kbexplorer_script",
+      ":chromevox_options_script",
+      ":chromevox_background_script",
+      ":chromevox_panel_script",
     ]
   } else {
     deps += [ ":chromevox_deps_js" ]
@@ -133,7 +134,7 @@
 run_jsbundler("chromevox_copied_files") {
   mode = "copy"
   dest_dir = chromevox_out_dir
-  sources = chromevox_assets_gypi_values.chromevox_assets_chromevox
+  sources = chromevox_assets_gypi_values.chromevox_assets_images
   sources +=
       chromevox_assets_gypi_values.chromevox_assets_chromevox_background_earcons
   sources +=
@@ -148,6 +149,7 @@
     "chromevox/background/options.html",
     "chromevox/injected/api.js",
     "cvox2/background/background.html",
+    "cvox2/background/panel.html",
   ]
   if (chromevox_compress_js) {
     sources += [ "chromevox/injected/api_util.js" ]
@@ -155,10 +157,11 @@
     sources += chromevox_modules
     sources += [
       "closure/closure_preinit.js",
-      chromevox_vars_gypi_values.chromevox1_content_script_loader_file,
-      chromevox_vars_gypi_values.chromevox1_kbexplorer_loader_file,
-      chromevox_vars_gypi_values.chromevox1_options_script_loader_file,
-      chromevox_vars_gypi_values.chromevox2_background_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_content_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_kbexplorer_loader_file,
+      chromevox_vars_gypi_values.chromevox_options_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_background_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_panel_script_loader_file,
     ]
   }
   if (!chromevox_compress_js) {
@@ -256,33 +259,40 @@
     }
   }
 
-  compress_js("chromevox1_content_script") {
+  compress_js("chromevox_content_script") {
     sources = [
-      chromevox_vars_gypi_values.chromevox1_content_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_content_script_loader_file,
     ]
     output_file = "$chromevox_out_dir/chromeVoxChromePageScript.js"
   }
 
-  compress_js("chromevox1_kbexplorer_script") {
+  compress_js("chromevox_kbexplorer_script") {
     sources = [
-      chromevox_vars_gypi_values.chromevox1_kbexplorer_loader_file,
+      chromevox_vars_gypi_values.chromevox_kbexplorer_loader_file,
     ]
     output_file = "$chromevox_out_dir/chromeVoxKbExplorerScript.js"
   }
 
-  compress_js("chromevox1_options_script") {
+  compress_js("chromevox_options_script") {
     sources = [
-      chromevox_vars_gypi_values.chromevox1_options_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_options_script_loader_file,
     ]
     output_file = "$chromevox_out_dir/chromeVoxChromeOptionsScript.js"
   }
 
-  compress_js("chromevox2_background_script") {
+  compress_js("chromevox_background_script") {
     sources = [
-      chromevox_vars_gypi_values.chromevox2_background_script_loader_file,
+      chromevox_vars_gypi_values.chromevox_background_script_loader_file,
     ]
     output_file = "$chromevox_out_dir/chromeVox2ChromeBackgroundScript.js"
   }
+
+  compress_js("chromevox_panel_script") {
+    sources = [
+      chromevox_vars_gypi_values.chromevox_panel_script_loader_file,
+    ]
+    output_file = "$chromevox_out_dir/chromeVoxPanelScript.js"
+  }
 } else {
   generate_deps_js("chromevox_deps_js") {
     sources = chromevox_modules + closure_library_modules
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox.gni b/chrome/browser/resources/chromeos/chromevox/chromevox.gni
index 6a36df8d..2a4e8c8b 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox.gni
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox.gni
@@ -131,6 +131,8 @@
   "cvox2/background/earcon_engine.js",
   "cvox2/background/next_earcons.js",
   "cvox2/background/output.js",
+  "cvox2/background/panel.js",
+  "cvox2/background/panel_command.js",
   "cvox2/background/tabs_automation_handler.js",
   "extensions/searchvox/abstract_result.js",
   "extensions/searchvox/constants.js",
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox.gyp b/chrome/browser/resources/chromeos/chromevox/chromevox.gyp
index 94b33f1..9cdc4f5f 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox.gyp
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox.gyp
@@ -44,10 +44,11 @@
             }],
             ['chromevox_compress_js==1', {
               'dependencies': [
-                'chromevox1_content_script',
-                'chromevox1_kbexplorer_script',
-                'chromevox1_options_script',
-                'chromevox2_background_script',
+                'chromevox_content_script',
+                'chromevox_kbexplorer_script',
+                'chromevox_options_script',
+                'chromevox_background_script',
+                'chromevox_panel_script',
               ],
             }, {  # chromevox_compress_js==0
               'dependencies': [
@@ -72,6 +73,7 @@
               'destination': '<(chromevox_dest_dir)/cvox2/background',
               'files': [
                 'cvox2/background/background.html',
+                'cvox2/background/panel.html',
               ],
             },
           ],
@@ -134,10 +136,11 @@
             'deps_js_output_file': '<(chromevox_dest_dir)/deps.js',
           },
           'sources': [
-            '<(chromevox1_content_script_loader_file)',
-            '<(chromevox1_kbexplorer_loader_file)',
-            '<(chromevox1_options_script_loader_file)',
-            '<(chromevox2_background_script_loader_file)',
+            '<(chromevox_content_script_loader_file)',
+            '<(chromevox_kbexplorer_loader_file)',
+            '<(chromevox_options_script_loader_file)',
+            '<(chromevox_background_script_loader_file)',
+            '<(chromevox_panel_script_loader_file)',
           ],
           'includes': ['generate_deps.gypi'],
         },
@@ -163,43 +166,52 @@
         ['chromevox_compress_js==1', {
           'targets': [
             {
-              'target_name': 'chromevox1_content_script',
+              'target_name': 'chromevox_content_script',
               'type': 'none',
               'variables': {
                 'output_file': '<(chromevox_dest_dir)/chromeVoxChromePageScript.js',
               },
-              'sources': [ '<(chromevox1_content_script_loader_file)' ],
+              'sources': [ '<(chromevox_content_script_loader_file)' ],
               'includes': [ 'compress_js.gypi', ],
             },
             {
-              'target_name': 'chromevox1_options_script',
+              'target_name': 'chromevox_options_script',
               'type': 'none',
               'variables': {
                 'output_file': '<(chromevox_dest_dir)/chromeVoxChromeOptionsScript.js',
               },
-              'sources': [ '<(chromevox1_options_script_loader_file)' ],
+              'sources': [ '<(chromevox_options_script_loader_file)' ],
               'includes': [ 'compress_js.gypi', ],
             },
             {
-              'target_name': 'chromevox1_kbexplorer_script',
+              'target_name': 'chromevox_kbexplorer_script',
               'type': 'none',
               'variables': {
                 'output_file': '<(chromevox_dest_dir)/chromeVoxKbExplorerScript.js',
               },
-              'sources': [ '<(chromevox1_kbexplorer_loader_file)' ],
+              'sources': [ '<(chromevox_kbexplorer_loader_file)' ],
               'includes': [ 'compress_js.gypi', ],
             },
             {
-              'target_name': 'chromevox2_background_script',
+              'target_name': 'chromevox_background_script',
               'type': 'none',
               'variables': {
                 'output_file': '<(chromevox_dest_dir)/chromeVox2ChromeBackgroundScript.js',
               },
               'sources': [
-                '<(chromevox2_background_script_loader_file)',
+                '<(chromevox_background_script_loader_file)',
               ],
               'includes': [ 'compress_js.gypi', ],
             },
+            {
+              'target_name': 'chromevox_panel_script',
+              'type': 'none',
+              'variables': {
+                'output_file': '<(chromevox_dest_dir)/chromeVoxPanelScript.js',
+              },
+              'sources': [ '<(chromevox_panel_script_loader_file)' ],
+              'includes': [ 'compress_js.gypi', ],
+            },
           ],
         }, {  # chromevox_compress_js==0
           'targets': [
@@ -210,10 +222,11 @@
                 'dest_dir': '<(chromevox_dest_dir)',
               },
               'sources': [
-                '<(chromevox1_content_script_loader_file)',
-                '<(chromevox1_kbexplorer_loader_file)',
-                '<(chromevox1_options_script_loader_file)',
-                '<(chromevox2_background_script_loader_file)',
+                '<(chromevox_content_script_loader_file)',
+                '<(chromevox_kbexplorer_loader_file)',
+                '<(chromevox_options_script_loader_file)',
+                '<(chromevox_background_script_loader_file)',
+                '<(chromevox_panel_script_loader_file)',
               ],
               'includes': [ 'copy_js.gypi', ],
             },
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/braille_captions_background.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/braille_captions_background.js
index 2c7fa18..a38d3b7 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/braille_captions_background.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/braille_captions_background.js
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** @fileoverview Sends braille content to a content script if the braille
- * captions feature is enabled.
+/** @fileoverview If the braille captions feature is enabled, sends
+ * braille content to the Panel on Chrome OS, or a content script on
+ * other platforms.
  */
 
 goog.provide('cvox.BrailleCaptionsBackground');
 
+goog.require('PanelCommand');
 goog.require('cvox.BrailleDisplayState');
 goog.require('cvox.ExtensionBridge');
 
@@ -65,11 +67,17 @@
     brailleChars += String.fromCharCode(
         self.BRAILLE_UNICODE_BLOCK_START | byteBuf[i]);
   }
-  cvox.ExtensionBridge.send({
-    message: 'BRAILLE_CAPTION',
-    text: text,
-    brailleChars: brailleChars
-  });
+
+  if (cvox.ChromeVox.isChromeOS) {
+    var data = {text: text, braille: brailleChars};
+    (new PanelCommand(PanelCommandType.UPDATE_BRAILLE, data)).send();
+  } else {
+    cvox.ExtensionBridge.send({
+      message: 'BRAILLE_CAPTION',
+      text: text,
+      brailleChars: brailleChars
+    });
+  }
 };
 
 
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
index 3aa7300..0164642 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -3,42 +3,24 @@
     {
       "command": "previousElement",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [37],
-          "metaKey": [true]
+          "keyCode": [37]
         }
       }
     },
     {
       "command": "previousLine",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [38],
-          "metaKey": [true]
+          "keyCode": [38]
         }
       }
     },
     {
       "command": "nextElement",
       "sequence": {
-        "keys": {
-          "keyCode": [39],
-          "metaKey": [true]
-        }
-      }
-    },
-    {
-      "command": "nextLine",
-      "sequence": {
-        "keys": {
-          "keyCode": [40],
-          "metaKey": [true]
-        }
-      }
-    },
-    {
-      "command": "nextCharacter",
-      "sequence": {
         "cvoxModifier": true,
         "keys": {
           "keyCode": [39]
@@ -46,11 +28,31 @@
       }
     },
     {
+      "command": "nextLine",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [40]
+        }
+      }
+    },
+    {
+      "command": "nextCharacter",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [39],
+          "shiftKey": [true]
+        }
+      }
+    },
+    {
       "command": "previousCharacter",
       "sequence": {
         "cvoxModifier": true,
         "keys": {
-          "keyCode": [37]
+          "keyCode": [37],
+          "shiftKey": [true]
         }
       }
     },
@@ -60,7 +62,8 @@
         "cvoxModifier": true,
         "keys": {
           "keyCode": [39],
-          "ctrlKey": [true]
+          "ctrlKey": [true],
+          "shiftKey": [true]
         }
       }
     },
@@ -70,25 +73,26 @@
         "cvoxModifier": true,
         "keys": {
           "keyCode": [37],
-          "ctrlKey": [true]
+          "ctrlKey": [true],
+          "shiftKey": [true]
         }
       }
     },
     {
       "command": "nextButton",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [66],
-          "metaKey": [true]
+          "keyCode": [66]
         }
       }
     },
     {
       "command": "previousButton",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [66],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -96,18 +100,18 @@
     {
       "command": "nextCheckBox",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [88],
-          "metaKey": [true]
+          "keyCode": [88]
         }
       }
     },
     {
       "command": "previousCheckBox",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [88],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -115,18 +119,18 @@
     {
       "command": "nextCombobox",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [67],
-          "metaKey": [true]
+          "keyCode": [67]
         }
       }
     },
     {
       "command": "previousCombobox",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [67],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -134,18 +138,18 @@
     {
       "command": "nextEditText",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [69],
-          "metaKey": [true]
+          "keyCode": [69]
         }
       }
     },
     {
       "command": "previousEditText",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [69],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -153,18 +157,18 @@
     {
       "command": "nextFormField",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [70],
-          "metaKey": [true]
+          "keyCode": [70]
         }
       }
     },
     {
       "command": "previousFormField",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [70],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -172,18 +176,18 @@
     {
       "command": "nextHeading",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [72],
-          "metaKey": [true]
+          "keyCode": [72]
         }
       }
     },
     {
       "command": "previousHeading",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [72],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -191,18 +195,18 @@
     {
       "command": "nextLink",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [67],
-          "metaKey": [true]
+          "keyCode": [67]
         }
       }
     },
     {
       "command": "previousLink",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [76],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -210,18 +214,18 @@
     {
       "command": "nextTable",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [84],
-          "metaKey": [true]
+          "keyCode": [84]
         }
       }
     },
     {
       "command": "previousTable",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [84],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -229,18 +233,18 @@
     {
       "command": "nextVisitedLink",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [86],
-          "metaKey": [true]
+          "keyCode": [86]
         }
       }
     },
     {
       "command": "previousVisitedLink",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [86],
-          "metaKey": [true],
           "shiftKey": [true]
         }
       }
@@ -248,9 +252,9 @@
     {
       "command": "goToEnd",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [39],
-          "metaKey": [true],
           "ctrlKey": [true]
         }
       }
@@ -258,9 +262,9 @@
     {
       "command": "goToBeginning",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
           "keyCode": [37],
-          "metaKey": [true],
           "ctrlKey": [true]
         }
       }
@@ -268,15 +272,6 @@
     {
       "command": "doDefault",
       "sequence": {
-        "keys": {
-          "keyCode": [32],
-          "metaKey": [true]
-        }
-      }
-    },
-    {
-      "command": "showContextMenu",
-      "sequence": {
         "cvoxModifier": true,
         "keys": {
           "keyCode": [32]
@@ -284,11 +279,21 @@
       }
     },
     {
+      "command": "showContextMenu",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [32],
+          "shitKey": [true]
+        }
+      }
+    },
+    {
       "command": "continuousRead",
       "sequence": {
+        "cvoxModifier": true,
         "keys": {
-          "keyCode": [82],
-          "metaKey": [true]
+          "keyCode": [82]
         }
       }
     },
@@ -297,7 +302,8 @@
       "sequence": {
         "cvoxModifier": true,
         "keys": {
-          "keyCode": [81]
+          "keyCode": [81],
+          "shitKey": [true]
         }
       }
     }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox_assets.gypi b/chrome/browser/resources/chromeos/chromevox/chromevox_assets.gypi
index 61c8479..f7a52346 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox_assets.gypi
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox_assets.gypi
@@ -4,11 +4,16 @@
 
 {
   'variables': {
-    'chromevox_assets_chromevox': [
-      'chromevox/chromevox-128.png',
-      'chromevox/chromevox-16.png',
-      'chromevox/chromevox-19.png',
-      'chromevox/chromevox-48.png',
+    'chromevox_assets_images': [
+      'images/chromevox-128.png',
+      'images/chromevox-16.png',
+      'images/chromevox-19.png',
+      'images/chromevox-48.png',
+      'images/close-19.png',
+      'images/close-hover-19.png',
+      'images/options-19.png',
+      'images/options-hover-19.png',
+      'images/triangle-6.png',
     ],
     'chromevox_assets_chromevox_background_earcons': [
       'chromevox/background/earcons/alert_modal.ogg',
@@ -92,9 +97,9 @@
       'type': 'none',
       'copies': [
         {
-          'destination': '<(chromevox_dest_dir)/chromevox',
+          'destination': '<(chromevox_dest_dir)/images',
           'files': [
-            '<@(chromevox_assets_chromevox)',
+            '<@(chromevox_assets_images)',
           ],
         },
         {
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox_vars.gypi b/chrome/browser/resources/chromeos/chromevox/chromevox_vars.gypi
index c6997ce..59dfde51 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox_vars.gypi
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox_vars.gypi
@@ -6,10 +6,11 @@
 
 {
   'variables': {
-    'chromevox1_content_script_loader_file': 'chromevox/injected/loader.js',
-    'chromevox1_kbexplorer_loader_file': 'chromevox/background/kbexplorer_loader.js',
-    'chromevox1_options_script_loader_file': 'chromevox/background/options_loader.js',
-    'chromevox2_background_script_loader_file': 'cvox2/background/loader.js',
+    'chromevox_content_script_loader_file': 'chromevox/injected/loader.js',
+    'chromevox_kbexplorer_loader_file': 'chromevox/background/kbexplorer_loader.js',
+    'chromevox_options_script_loader_file': 'chromevox/background/options_loader.js',
+    'chromevox_background_script_loader_file': 'cvox2/background/loader.js',
+    'chromevox_panel_script_loader_file': 'cvox2/background/panel_loader.js',
     'chromevox_extension_key': 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDltVl1k15pjRzuZfMc3B69inxwm2bZeZ2O8/zFO+NluHnBm3GJ3fzdOoFGJd+M16I8p7zxxQyHeDMfWYASyCeB8XnUEDKjqNLQfCnncsANzHsYoEbYj2nEUML2P13b9q+AAvpCBpAJ4cZp81e9n1y/vbSXHE4385cgkKueItzikQIDAQAB',
   }
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
index 424cfda..f09e724 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -121,6 +121,14 @@
     }).bind(this)
   });
 
+  Object.defineProperty(cvox.ChromeVox, 'modKeyStr', {
+    get: function() {
+      return (this.mode_ == ChromeVoxMode.CLASSIC || this.mode_ ==
+              ChromeVoxMode.COMPAT) ?
+          'Search+Shift' : 'Search';
+    }.bind(this)
+  });
+
   document.addEventListener(
       'keydown', cvox.ChromeVoxKbHandler.basicKeyDownActionsListener, true);
   cvox.ChromeVoxKbHandler.commandHandler = this.onGotCommand.bind(this);
@@ -353,8 +361,7 @@
         }
         break;
       case 'showOptionsPage':
-        var optionsPage = {url: 'chromevox/background/options.html'};
-        chrome.tabs.create(optionsPage);
+        chrome.runtime.openOptionsPage();
         break;
       case 'toggleChromeVoxVersion':
         var newMode;
@@ -412,6 +419,14 @@
   },
 
   /**
+   * Open the options page in a new tab.
+   */
+  showOptionsPage: function() {
+    var optionsPage = {url: 'chromevox/background/options.html'};
+    chrome.tabs.create(optionsPage);
+  },
+
+  /**
    * Handles a braille command.
    * @param {!cvox.BrailleKeyEvent} evt
    * @param {!cvox.NavBraille} content
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.html b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.html
new file mode 100644
index 0000000..454e67c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.html
@@ -0,0 +1,149 @@
+<!-- Copyright 2015 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<html>
+<head>
+<meta charset="utf-8">
+<title class="i18n" msgid="panel_title"></title>
+
+<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono|Roboto:400,700,700italic' rel='stylesheet' type='text/css'>
+
+<script type="text/javascript" src="../../closure/base.js"></script>
+<script type="text/javascript" src="../../deps.js"></script>
+<script type="text/javascript" src="panel_loader.js"></script>
+<script type="text/javascript" src="../../chromeVoxPanelScript.js">
+</script>
+<style>
+  body {
+    padding: 0;
+    border: 0;
+    margin: 0;
+    height: 35px;
+    overflow: hidden;
+  }
+  #main {
+    position: fixed;
+    left: 0;
+    top: 0;
+    right: 0;
+    height: 35px;
+    background-color: #000;
+    display: flex;
+  }
+  button {
+    border: 0;
+    padding: 0;
+    margin: 0;
+    background-color: #000;
+    outline: none;
+  }
+  #menu {
+    min-width: 52px;
+    height: 35px;
+  }
+  #menu:hover {
+    background-color: #f69139;
+  }
+  #options_close {
+    min-width: 48px;
+    height: 35px;
+  }
+  #chromevox {
+    width: 19px;
+    height: 19px;
+    margin: 8px 0;
+  }
+  #triangle {
+    width: 6px;
+    height: 6px;
+    margin: 14px 0;
+  }
+  #options {
+    background-image: url('/images/options-19.png');
+    width: 19px;
+    height: 19px;
+    margin: 8px 0;
+  }
+  #options:hover {
+    background-image: url('/images/options-hover-19.png');
+  }
+  #close {
+    background-image: url('/images/close-19.png');
+    width: 19px;
+    height: 19px;
+  }
+  #close:hover {
+    background-image: url('/images/close-hover-19.png');
+  }
+  #caption {
+    color: #fff;
+    margin-left: 12px;
+    flex-grow: 1;
+  }
+  #speech-container {
+    height: 35px;
+    margin-left: 12px;
+  }
+  #speech {
+    height: 35px;
+    line-height: 35px;
+    font-family: 'Roboto', sans-serif;
+    font-size: 12pt;
+    overflow: hidden;
+  }
+  .usertext {
+    font-weight: 700;
+    font-style: italic;
+  }
+  #braille-container {
+    position: relative;
+    top: -35px;
+    height: 35px;
+  }
+  #braille-text {
+    height: 17px;
+    line-height: 17px;
+    font-family: 'Droid Sans Mono', Courier, monospace;
+    font-size: 12pt;
+    letter-spacing: 3.13px;
+    white-space: pre;
+  }
+  #braille-cells {
+    height: 17px;
+    line-height: 17px;
+    font-family: 'Droid Sans Mono', Courier, monospace;
+    font-size: 13pt;
+    white-space: pre;
+  }
+</style>
+
+</head>
+
+<html>
+
+<body>
+<div id="main">
+  <div hidden id="menu_title" class="i18n" msgid="menu_title"></div>
+  <button id="menu" aria-labelledby="menu_title">
+    <img id="chromevox" src="/images/chromevox-19.png">
+    <img id="triangle" src="/images/triangle-6.png">
+  </button>
+  <div id="caption">
+    <div id="speech-container">
+      <div id="speech"></div>
+    </div>
+    <div id="braille-container">
+      <div id="braille-text"></div>
+      <div id="braille-cells"></div>
+    </div>
+  </div>
+  <div id="options_close">
+    <div hidden id="options_title" class="i18n" msgid="options"></div>
+    <button id="options" aria-labelledby="options_title"></button>
+    <div hidden id="disable_chromevox_title" class="i18n"
+         msgid="disable_chromevox"></div>
+    <button id="close" aria-labelledby="disable_chromevox_title"></button>
+  </div>
+</div>
+</body>
+</html>
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js
new file mode 100644
index 0000000..286fc92
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel.js
@@ -0,0 +1,150 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview ChromeVox panel.
+ *
+ */
+
+goog.provide('Panel');
+
+goog.require('Msgs');
+goog.require('PanelCommand');
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+/**
+ * Class to manage the panel.
+ * @constructor
+ */
+Panel = function() {
+};
+
+/**
+ * Initialize the panel.
+ */
+Panel.init = function() {
+  /** @type {Element} @private */
+  this.speechContainer_ = $('speech-container');
+
+  /** @type {Element} @private */
+  this.speechElement_ = $('speech');
+
+  /** @type {Element} @private */
+  this.brailleContainer_ = $('braille-container');
+
+  /** @type {Element} @private */
+  this.brailleTextElement_ = $('braille-text');
+
+  /** @type {Element} @private */
+  this.brailleCellsElement_ = $('braille-cells');
+
+  Panel.updateFromPrefs();
+  window.addEventListener('storage', function(event) {
+    if (event.key == 'brailleCaptions') {
+      Panel.updateFromPrefs();
+    }
+  }, false);
+
+  window.addEventListener('message', function(message) {
+    var command = JSON.parse(message.data);
+    Panel.exec(/** @type {PanelCommand} */(command));
+  }, false);
+
+  $('menu').addEventListener('click', Panel.onMenu, false);
+  $('options').addEventListener('click', Panel.onOptions, false);
+  $('close').addEventListener('click', Panel.onClose, false);
+
+  Msgs.addTranslatedMessagesToDom(document);
+};
+
+/**
+ * Update the display based on prefs.
+ */
+Panel.updateFromPrefs = function() {
+  if (localStorage['brailleCaptions'] === String(true)) {
+    this.speechContainer_.style.visibility = 'hidden';
+    this.brailleContainer_.style.visibility = 'visible';
+  } else {
+    this.speechContainer_.style.visibility = 'visible';
+    this.brailleContainer_.style.visibility = 'hidden';
+  }
+};
+
+/**
+ * Execute a command to update the panel.
+ *
+ * @param {PanelCommand} command The command to execute.
+ */
+Panel.exec = function(command) {
+  /**
+   * Escape text so it can be safely added to HTML.
+   * @param {*} str Text to be added to HTML, will be cast to string.
+   * @return {string} The escaped string.
+   */
+  function escapeForHtml(str) {
+    return String(str)
+        .replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/\>/g, '&gt;')
+        .replace(/"/g, '&quot;')
+        .replace(/'/g, '&#039;')
+        .replace(/\//g, '&#x2F;');
+  }
+
+  switch (command.type) {
+    case PanelCommandType.CLEAR_SPEECH:
+      this.speechElement_.innerHTML = '';
+      break;
+    case PanelCommandType.ADD_NORMAL_SPEECH:
+      if (this.speechElement_.innerHTML != '') {
+        this.speechElement_.innerHTML += '&nbsp;&nbsp;';
+      }
+      this.speechElement_.innerHTML += '<span class="usertext">' +
+                                       escapeForHtml(command.data) +
+                                       '</span>';
+      break;
+    case PanelCommandType.ADD_ANNOTATION_SPEECH:
+      if (this.speechElement_.innerHTML != '') {
+        this.speechElement_.innerHTML += '&nbsp;&nbsp;';
+      }
+      this.speechElement_.innerHTML += escapeForHtml(command.data);
+      break;
+    case PanelCommandType.UPDATE_BRAILLE:
+      this.brailleTextElement_.textContent = command.data.text;
+      this.brailleCellsElement_.textContent = command.data.braille;
+      break;
+  }
+};
+
+/**
+ * Open the ChromeVox Menu.
+ */
+Panel.onMenu = function() {
+  window.location = '#fullscreen';
+  // TODO(dmazzoni): implement the menu UI here.
+};
+
+/**
+ * Open the ChromeVox Options.
+ */
+Panel.onOptions = function() {
+  var bkgnd =
+      chrome.extension.getBackgroundPage()['global']['backgroundObj'];
+  bkgnd['showOptionsPage']();
+  window.location = '#';
+};
+
+/**
+ * Exit ChromeVox.
+ */
+Panel.onClose = function() {
+  window.location = '#close';
+};
+
+window.addEventListener('load', function() {
+  Panel.init();
+}, false);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_command.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_command.js
new file mode 100644
index 0000000..473a305
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_command.js
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Commands to pass from the ChromeVox background page context
+ * to the ChromeVox Panel.
+ */
+
+goog.provide('PanelCommand');
+goog.provide('PanelCommandType');
+
+/**
+ * Create one command to pass to the ChromeVox Panel.
+ * @param {PanelCommandType} type The type of command.
+ * @param {string|{text: string, braille: string}=} opt_data
+ *     Optional data associated with the command.
+ * @constructor
+ */
+PanelCommand = function(type, opt_data) {
+  this.type = type;
+  this.data = opt_data;
+};
+
+/**
+ * Send this command to the ChromeVox Panel window.
+ */
+PanelCommand.prototype.send = function() {
+  var views = chrome.extension.getViews();
+  for (var i = 0; i < views.length; i++) {
+    if (views[i].location.href.indexOf('background/panel.html') > 0) {
+      views[i].postMessage(JSON.stringify(this), window.location.origin);
+    }
+  }
+};
+
+/**
+ * Possible panel commands.
+ * @enum {string}
+ */
+PanelCommandType = {
+  CLEAR_SPEECH: 'clear_speech',
+  ADD_NORMAL_SPEECH: 'add_normal_speech',
+  ADD_ANNOTATION_SPEECH: 'add_annotation_speech',
+  UPDATE_BRAILLE: 'update_braille',
+};
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_loader.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_loader.js
new file mode 100644
index 0000000..1d7156c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_loader.js
@@ -0,0 +1,10 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Loads the panel script.
+ *
+ */
+
+goog.require('Panel');
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background.js b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background.js
index 7edb8720..a70c7588 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background.js
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/tts_background.js
@@ -10,6 +10,7 @@
 
 goog.provide('cvox.TtsBackground');
 
+goog.require('PanelCommand');
 goog.require('cvox.AbstractTts');
 goog.require('cvox.ChromeTtsBase');
 goog.require('cvox.ChromeVox');
@@ -301,6 +302,9 @@
   // make a note that we're going to stop speech.
   if (queueMode == cvox.QueueMode.FLUSH ||
       queueMode == cvox.QueueMode.CATEGORY_FLUSH) {
+    (new PanelCommand(
+        PanelCommandType.CLEAR_SPEECH)).send();
+
     if (this.shouldCancel_(this.currentUtterance_, utterance, queueMode)) {
       this.cancelUtterance_(this.currentUtterance_);
       this.currentUtterance_ = null;
@@ -320,6 +324,19 @@
   // Next, add the new utterance to the queue.
   this.utteranceQueue_.push(utterance);
 
+  // Update the caption panel.
+  if (utterance.properties &&
+      utterance.properties['pitch'] &&
+      utterance.properties['pitch'] < this.ttsProperties['pitch']) {
+    (new PanelCommand(
+        PanelCommandType.ADD_ANNOTATION_SPEECH,
+        utterance.textString)).send();
+  } else {
+    (new PanelCommand(
+        PanelCommandType.ADD_NORMAL_SPEECH,
+        utterance.textString)).send();
+  }
+
   // Now start speaking the next item in the queue.
   this.startSpeakingNextItemInQueue_();
 };
@@ -499,6 +516,7 @@
 
   this.utteranceQueue_.length = 0;
 
+  (new PanelCommand(PanelCommandType.CLEAR_SPEECH)).send();
   chrome.tts.stop();
 };
 
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-128.png b/chrome/browser/resources/chromeos/chromevox/images/chromevox-128.png
similarity index 100%
rename from chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-128.png
rename to chrome/browser/resources/chromeos/chromevox/images/chromevox-128.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-16.png b/chrome/browser/resources/chromeos/chromevox/images/chromevox-16.png
similarity index 100%
rename from chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-16.png
rename to chrome/browser/resources/chromeos/chromevox/images/chromevox-16.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-19.png b/chrome/browser/resources/chromeos/chromevox/images/chromevox-19.png
similarity index 100%
rename from chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-19.png
rename to chrome/browser/resources/chromeos/chromevox/images/chromevox-19.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-48.png b/chrome/browser/resources/chromeos/chromevox/images/chromevox-48.png
similarity index 100%
rename from chrome/browser/resources/chromeos/chromevox/chromevox/chromevox-48.png
rename to chrome/browser/resources/chromeos/chromevox/images/chromevox-48.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/close-19.png b/chrome/browser/resources/chromeos/chromevox/images/close-19.png
new file mode 100644
index 0000000..83a62c87
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/close-19.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/close-hover-19.png b/chrome/browser/resources/chromeos/chromevox/images/close-hover-19.png
new file mode 100644
index 0000000..96cf00b8
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/close-hover-19.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/options-19.png b/chrome/browser/resources/chromeos/chromevox/images/options-19.png
new file mode 100644
index 0000000..6187118
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/options-19.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/options-hover-19.png b/chrome/browser/resources/chromeos/chromevox/images/options-hover-19.png
new file mode 100644
index 0000000..660cb7ec
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/options-hover-19.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/options.png b/chrome/browser/resources/chromeos/chromevox/images/options.png
new file mode 100644
index 0000000..5b3fc68
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/options.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/images/triangle-6.png b/chrome/browser/resources/chromeos/chromevox/images/triangle-6.png
new file mode 100644
index 0000000..f003a1d2
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/images/triangle-6.png
Binary files differ
diff --git a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2 b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
index ec3777e..5618c7fc 100644
--- a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
+++ b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
@@ -91,5 +91,10 @@
     }
   },
   "options_page": "chromevox/background/options.html",
-  "default_locale": "en"
+  "default_locale": "en",
+  "icons": {
+    "16": "images/chromevox-16.png",
+    "48": "images/chromevox-48.png",
+    "128": "images/chromevox-128.png"
+  }
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index b46310f..d1b911c7 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -2397,6 +2397,20 @@
       <message desc="Describes the volume up key in the ChromeVox keyboard explorer." name="IDS_CHROMEVOX_VOLUME_UP_KEY">
         volume up
       </message>
+
+      <!-- Panel -->
+      <message desc="Title of the ChromeVox panel, a window that displays the text ChromeVox is speaking and contains controls to manipulate ChromeVox." name="IDS_CHROMEVOX_PANEL_TITLE">
+        ChromeVox Panel
+      </message>
+      <message desc="Title of the button that opens up the ChromeVox menu." name="IDS_CHROMEVOX_MENU_TITLE">
+        ChromeVox Menu
+      </message>
+      <message desc="Title of the button that opens up the ChromeVox menu." name="IDS_CHROMEVOX_OPTIONS">
+        ChromeVox Options
+      </message>
+      <message desc="Title of the button that disables ChromeVox." name="IDS_CHROMEVOX_DISABLE_CHROMEVOX">
+        Disable ChromeVox
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py b/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py
index bb5cb4d..1ff2f26 100755
--- a/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py
+++ b/chrome/browser/resources/chromeos/chromevox/tools/check_chromevox.py
@@ -61,6 +61,7 @@
     [[CVoxPath('chromevox/background/options_loader.js')], _COMMON_EXTERNS],
     [[CVoxPath('chromevox/injected/loader.js')], _COMMON_EXTERNS],
     [[CVoxPath('cvox2/background/loader.js')], _COMMON_EXTERNS],
+    [[CVoxPath('cvox2/background/panel_loader.js')], _COMMON_EXTERNS],
     ]
 
 
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay_data.js b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
index 771221a..f929a27 100644
--- a/chrome/browser/resources/chromeos/keyboard_overlay_data.js
+++ b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
@@ -15776,7 +15776,6 @@
     '-<>CTRL<>SHIFT': 'keyboardOverlayZoomScreenOut',
     '-<>SEARCH': 'keyboardOverlayF11',
     '.<>ALT<>CTRL': 'keyboardOverlayNextUser',
-    '.<>CTRL<>SHIFT': 'keyboardOverlayToggleSpeechInput',
     '.<>SEARCH': 'keyboardOverlayInsert',
     '/<>ALT<>CTRL': 'keyboardOverlayViewKeyboardOverlay',
     '/<>ALT<>CTRL<>SHIFT': 'keyboardOverlayViewKeyboardOverlay',
diff --git a/chrome/browser/resources/settings/a11y_page/a11y_page.html b/chrome/browser/resources/settings/a11y_page/a11y_page.html
index 5afc4c7..fbea6e3 100644
--- a/chrome/browser/resources/settings/a11y_page/a11y_page.html
+++ b/chrome/browser/resources/settings/a11y_page/a11y_page.html
@@ -21,10 +21,10 @@
     </settings-checkbox>
     <settings-checkbox
         pref="{{prefs.settings.a11y.sticky_keys_enabled}}"
-        i18n-values="label:stickyKeysLabel; subLabel:stickyKeysSublabel">
+        i18n-values="label:stickyKeysLabel">
     </settings-checkbox>
     <settings-checkbox pref="{{prefs.settings.accessibility}}"
-        i18n-values="label:chromeVoxLabel; subLabel:chromeVoxSublabel">
+        i18n-values="label:chromeVoxLabel">
     </settings-checkbox>
     <settings-checkbox i18n-values="label:screenMagnifierLabel"
         pref="{{prefs.settings.a11y.screen_magnifier}}">
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 83ecc0b..c78428c8 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -778,9 +778,6 @@
     case IDC_SHOW_SIGNIN:
       ShowBrowserSigninOrSettings(browser_, signin_metrics::SOURCE_MENU);
       break;
-    case IDC_TOGGLE_SPEECH_INPUT:
-      ToggleSpeechInput(browser_);
-      break;
     case IDC_DISTILL_PAGE:
       DistillCurrentPage(browser_);
       break;
@@ -1036,9 +1033,6 @@
   command_updater_.UpdateCommandEnabled(IDC_UPGRADE_DIALOG, true);
   command_updater_.UpdateCommandEnabled(IDC_VIEW_INCOMPATIBILITIES, true);
 
-  // Toggle speech input
-  command_updater_.UpdateCommandEnabled(IDC_TOGGLE_SPEECH_INPUT, true);
-
   // Distill current page.
   command_updater_.UpdateCommandEnabled(
       IDC_DISTILL_PAGE, base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 12f21b3..8b7e06b 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -1131,15 +1131,6 @@
   }
 }
 
-void ToggleSpeechInput(Browser* browser) {
-  SearchTabHelper* search_tab_helper =
-      SearchTabHelper::FromWebContents(
-          browser->tab_strip_model()->GetActiveWebContents());
-  // |search_tab_helper| can be null in unit tests.
-  if (search_tab_helper)
-    search_tab_helper->ToggleVoiceSearch();
-}
-
 void DistillCurrentPage(Browser* browser) {
   DistillCurrentPageAndView(browser->tab_strip_model()->GetActiveWebContents());
 }
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 3b32291..153188d 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -147,7 +147,6 @@
 void ShowAvatarMenu(Browser* browser);
 void ShowFastUserSwitcher(Browser* browser);
 void OpenUpdateChromeDialog(Browser* browser);
-void ToggleSpeechInput(Browser* browser);
 void DistillCurrentPage(Browser* browser);
 bool CanRequestTabletSite(content::WebContents* current_tab);
 bool IsRequestingTabletSite(Browser* browser);
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
index 692a360..b2adbb32 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
@@ -17,7 +17,6 @@
 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.h"
-#include "chrome/browser/ui/search/search_model_observer.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/ui/zoom/zoom_event_manager_observer.h"
 
@@ -29,7 +28,6 @@
 class LocationBarDecoration;
 class LocationIconDecoration;
 class ManagePasswordsDecoration;
-class MicSearchDecoration;
 class PageActionDecoration;
 class Profile;
 class SelectedKeywordDecoration;
@@ -45,7 +43,6 @@
 class LocationBarViewMac : public LocationBar,
                            public LocationBarTesting,
                            public ChromeOmniboxEditController,
-                           public SearchModelObserver,
                            public ui_zoom::ZoomEventManagerObserver {
  public:
   LocationBarViewMac(AutocompleteTextField* field,
@@ -172,10 +169,6 @@
     return manage_passwords_decoration_.get();
   }
 
-  // SearchModelObserver:
-  void ModelChanged(const SearchModel::State& old_state,
-                    const SearchModel::State& new_state) override;
-
   Browser* browser() const { return browser_; }
 
   // ZoomManagerObserver:
@@ -219,10 +212,6 @@
   // Returns whether any updates were made.
   bool UpdateZoomDecoration(bool default_zoom_changed);
 
-  // Updates the voice search decoration. Returns true if the visible state was
-  // changed.
-  bool UpdateMicSearchDecorationVisibility();
-
   scoped_ptr<OmniboxViewMac> omnibox_view_;
 
   AutocompleteTextField* field_;  // owned by tab controller
@@ -256,9 +245,6 @@
   // Keyword hint decoration displayed on the right-hand side.
   scoped_ptr<KeywordHintDecoration> keyword_hint_decoration_;
 
-  // The voice search icon.
-  scoped_ptr<MicSearchDecoration> mic_search_decoration_;
-
   // The right-hand-side button to manage passwords associated with a page.
   scoped_ptr<ManagePasswordsDecoration> manage_passwords_decoration_;
 
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
index 780c1a3f..dd22784 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -40,7 +40,6 @@
 #import "chrome/browser/ui/cocoa/location_bar/keyword_hint_decoration.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_icon_decoration.h"
 #import "chrome/browser/ui/cocoa/location_bar/manage_passwords_decoration.h"
-#import "chrome/browser/ui/cocoa/location_bar/mic_search_decoration.h"
 #import "chrome/browser/ui/cocoa/location_bar/page_action_decoration.h"
 #import "chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.h"
 #import "chrome/browser/ui/cocoa/location_bar/star_decoration.h"
@@ -102,7 +101,6 @@
       translate_decoration_(new TranslateDecoration(command_updater)),
       zoom_decoration_(new ZoomDecoration(this)),
       keyword_hint_decoration_(new KeywordHintDecoration()),
-      mic_search_decoration_(new MicSearchDecoration(command_updater)),
       manage_passwords_decoration_(
           new ManagePasswordsDecoration(command_updater, this)),
       browser_(browser),
@@ -122,8 +120,6 @@
       base::Bind(&LocationBarViewMac::OnEditBookmarksEnabledChanged,
                  base::Unretained(this)));
 
-  browser_->search_model()->AddObserver(this);
-
   ui_zoom::ZoomEventManager::GetForBrowserContext(profile)
       ->AddZoomEventManagerObserver(this);
 
@@ -139,7 +135,6 @@
   // Disconnect from cell in case it outlives us.
   [[field_ cell] clearDecorations];
 
-  browser_->search_model()->RemoveObserver(this);
   ui_zoom::ZoomEventManager::GetForBrowserContext(profile())
       ->RemoveZoomEventManagerObserver(this);
 }
@@ -413,7 +408,6 @@
   }
 
   [cell addRightDecoration:keyword_hint_decoration_.get()];
-  [cell addRightDecoration:mic_search_decoration_.get()];
 
   // By default only the location icon is visible.
   location_icon_decoration_->SetVisible(true);
@@ -526,7 +520,6 @@
   UpdateZoomDecoration(/*default_zoom_changed=*/false);
   RefreshPageActionDecorations();
   RefreshContentSettingsDecorations();
-  UpdateMicSearchDecorationVisibility();
   if (contents)
     omnibox_view_->OnTabChanged(contents);
   else
@@ -587,12 +580,6 @@
   return OmniboxViewMac::ImageForResource(IDR_OMNIBOX_SEARCH);
 }
 
-void LocationBarViewMac::ModelChanged(const SearchModel::State& old_state,
-                                      const SearchModel::State& new_state) {
-  if (UpdateMicSearchDecorationVisibility())
-    Layout();
-}
-
 void LocationBarViewMac::PostNotification(NSString* notification) {
   [[NSNotificationCenter defaultCenter] postNotificationName:notification
                                         object:[NSValue valueWithPointer:this]];
@@ -730,12 +717,3 @@
   if (UpdateZoomDecoration(/*default_zoom_changed=*/true))
     OnDecorationsChanged();
 }
-
-bool LocationBarViewMac::UpdateMicSearchDecorationVisibility() {
-  bool is_visible = !GetToolbarModel()->input_in_progress() &&
-                    browser_->search_model()->voice_search_supported();
-  if (mic_search_decoration_->IsVisible() == is_visible)
-    return false;
-  mic_search_decoration_->SetVisible(is_visible);
-  return true;
-}
diff --git a/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.h b/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.h
deleted file mode 100644
index 91c05cc..0000000
--- a/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_LOCATION_BAR_MIC_SEARCH_DECORATION_H_
-#define CHROME_BROWSER_UI_COCOA_LOCATION_BAR_MIC_SEARCH_DECORATION_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "chrome/browser/ui/cocoa/location_bar/image_decoration.h"
-
-class CommandUpdater;
-
-// Draws a microphone icon on the right side of the omnibox. This is used for
-// voice search.
-class MicSearchDecoration : public ImageDecoration {
- public:
-  explicit MicSearchDecoration(CommandUpdater* command_updater);
-  ~MicSearchDecoration() override;
-
-  // Implement |LocationBarDecoration|.
-  bool AcceptsMousePress() override;
-  bool OnMousePressed(NSRect frame, NSPoint location) override;
-  NSString* GetToolTip() override;
-
- private:
-  CommandUpdater* command_updater_;  // Weak, owned by Browser.
-
-  DISALLOW_COPY_AND_ASSIGN(MicSearchDecoration);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_LOCATION_BAR_MIC_SEARCH_DECORATION_H_
diff --git a/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.mm b/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.mm
deleted file mode 100644
index f8b01849..0000000
--- a/chrome/browser/ui/cocoa/location_bar/mic_search_decoration.mm
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/location_bar/mic_search_decoration.h"
-
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/command_updater.h"
-#import "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
-#include "chrome/grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-MicSearchDecoration::MicSearchDecoration(CommandUpdater* command_updater)
-    : command_updater_(command_updater) {
-  SetImage(OmniboxViewMac::ImageForResource(IDR_OMNIBOX_MIC_SEARCH));
-}
-
-MicSearchDecoration::~MicSearchDecoration() {
-}
-
-bool MicSearchDecoration::AcceptsMousePress() {
-  return true;
-}
-
-bool MicSearchDecoration::OnMousePressed(NSRect frame, NSPoint location) {
-  command_updater_->ExecuteCommand(IDC_TOGGLE_SPEECH_INPUT);
-  return true;
-}
-
-NSString* MicSearchDecoration::GetToolTip() {
-  return l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_MIC_SEARCH);
-}
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
index b61581e..eb8de39 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
@@ -196,7 +196,7 @@
       // window. To fix, explicitly set the tab's new location so that it's
       // correct at tearoff time. See http://crbug.com/541674 .
       NSRect newTabFrame = [[draggedTab_ tabView] frame];
-      newTabFrame.origin.x = sourceTabFrame_.origin.x + offset;
+      newTabFrame.origin.x = trunc(sourceTabFrame_.origin.x + offset);
 
       // Ensure that the tab won't extend beyond the right edge of the tab area
       // in the tab strip.
diff --git a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
index e7655be..5d99c136 100644
--- a/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
+++ b/chrome/browser/ui/cocoa/view_id_util_browsertest.mm
@@ -72,7 +72,6 @@
           i == VIEW_ID_BROWSER_ACTION ||
           i == VIEW_ID_FEEDBACK_BUTTON ||
           i == VIEW_ID_SCRIPT_BUBBLE ||
-          i == VIEW_ID_MIC_SEARCH_BUTTON ||
           i == VIEW_ID_SAVE_CREDIT_CARD_BUTTON ||
           i == VIEW_ID_TRANSLATE_BUTTON) {
         continue;
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index 34a519e..44167e8 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -139,9 +139,8 @@
         submit_count_(0),
         on_esc_key_press_event_calls_(0),
         on_focus_changed_calls_(0),
-        is_focused_(false),
-        on_toggle_voice_search_calls_(0) {
-  }
+        is_focused_(false) {}
+
  protected:
   void SetUpInProcessBrowserTestFixture() override {
     search::EnableQueryExtractionForTesting();
@@ -185,8 +184,6 @@
                        &on_focus_changed_calls_) &&
            GetBoolFromJS(contents, "isFocused",
                          &is_focused_) &&
-           GetIntFromJS(contents, "onToggleVoiceSearchCalls",
-                        &on_toggle_voice_search_calls_) &&
            GetStringFromJS(contents, "prefetchQuery", &prefetch_query_value_);
 
   }
@@ -247,7 +244,6 @@
   std::string query_value_;
   int on_focus_changed_calls_;
   bool is_focused_;
-  int on_toggle_voice_search_calls_;
   std::string prefetch_query_value_;
 };
 
diff --git a/chrome/browser/ui/search/search_ipc_router.cc b/chrome/browser/ui/search/search_ipc_router.cc
index 72598b2..18de364 100644
--- a/chrome/browser/ui/search/search_ipc_router.cc
+++ b/chrome/browser/ui/search/search_ipc_router.cc
@@ -137,13 +137,6 @@
   Send(new ChromeViewMsg_SearchBoxThemeChanged(routing_id(), theme_info));
 }
 
-void SearchIPCRouter::ToggleVoiceSearch() {
-  if (!policy_->ShouldSendToggleVoiceSearch())
-    return;
-
-  Send(new ChromeViewMsg_SearchBoxToggleVoiceSearch(routing_id()));
-}
-
 void SearchIPCRouter::Submit(const base::string16& text,
                              const EmbeddedSearchRequestParams& params) {
   if (!policy_->ShouldSubmitQuery())
@@ -173,8 +166,6 @@
   IPC_BEGIN_MESSAGE_MAP(SearchIPCRouter, message)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_InstantSupportDetermined,
                         OnInstantSupportDetermined)
-    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SetVoiceSearchSupported,
-                        OnVoiceSearchSupportDetermined)
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusOmnibox, OnFocusOmnibox);
     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxNavigate,
                         OnSearchBoxNavigate);
@@ -208,19 +199,6 @@
   delegate_->OnInstantSupportDetermined(instant_support);
 }
 
-void SearchIPCRouter::OnVoiceSearchSupportDetermined(
-    int page_seq_no,
-    bool supports_voice_search) const {
-  if (page_seq_no != commit_counter_)
-    return;
-
-  delegate_->OnInstantSupportDetermined(true);
-  if (!policy_->ShouldProcessSetVoiceSearchSupport())
-    return;
-
-  delegate_->OnSetVoiceSearchSupport(supports_voice_search);
-}
-
 void SearchIPCRouter::OnFocusOmnibox(int page_seq_no,
                                      OmniboxFocusState state) const {
   if (page_seq_no != commit_counter_)
diff --git a/chrome/browser/ui/search/search_ipc_router.h b/chrome/browser/ui/search/search_ipc_router.h
index 0f62aba..e0f3749 100644
--- a/chrome/browser/ui/search/search_ipc_router.h
+++ b/chrome/browser/ui/search/search_ipc_router.h
@@ -36,9 +36,6 @@
     // load event.
     virtual void OnInstantSupportDetermined(bool supports_instant) = 0;
 
-    // Called upon determination of voice search API support.
-    virtual void OnSetVoiceSearchSupport(bool supports_voice_search) = 0;
-
     // Called when the page wants the omnibox to be focused. |state| specifies
     // the omnibox focus state.
     virtual void FocusOmnibox(OmniboxFocusState state) = 0;
@@ -100,7 +97,6 @@
 
     // SearchIPCRouter calls these functions before sending/receiving messages
     // to/from the page.
-    virtual bool ShouldProcessSetVoiceSearchSupport() = 0;
     virtual bool ShouldProcessFocusOmnibox(bool is_active_tab) = 0;
     virtual bool ShouldProcessNavigateToURL(bool is_active_tab) = 0;
     virtual bool ShouldProcessDeleteMostVisitedItem() = 0;
@@ -118,7 +114,6 @@
     virtual bool ShouldSendOmniboxFocusChanged() = 0;
     virtual bool ShouldSendMostVisitedItems() = 0;
     virtual bool ShouldSendThemeBackgroundInfo() = 0;
-    virtual bool ShouldSendToggleVoiceSearch() = 0;
     virtual bool ShouldSubmitQuery() = 0;
   };
 
@@ -167,9 +162,6 @@
   // Tells the renderer about the current theme background.
   void SendThemeBackgroundInfo(const ThemeBackgroundInfo& theme_info);
 
-  // Tells the page to toggle voice search.
-  void ToggleVoiceSearch();
-
   // Tells the page that the user pressed Enter in the omnibox.
   void Submit(const base::string16& text,
               const EmbeddedSearchRequestParams& params);
@@ -199,8 +191,6 @@
   bool OnMessageReceived(const IPC::Message& message) override;
 
   void OnInstantSupportDetermined(int page_seq_no, bool supports_instant) const;
-  void OnVoiceSearchSupportDetermined(int page_id,
-                                      bool supports_voice_search) const;
   void OnFocusOmnibox(int page_id, OmniboxFocusState state) const;
   void OnSearchBoxNavigate(int page_id,
                            const GURL& url,
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
index 4a85e2fb..382038a0 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.cc
@@ -22,10 +22,6 @@
 
 SearchIPCRouterPolicyImpl::~SearchIPCRouterPolicyImpl() {}
 
-bool SearchIPCRouterPolicyImpl::ShouldProcessSetVoiceSearchSupport() {
-  return true;
-}
-
 bool SearchIPCRouterPolicyImpl::ShouldProcessFocusOmnibox(bool is_active_tab) {
   return is_active_tab && !is_incognito_ && search::IsInstantNTP(web_contents_);
 }
@@ -96,10 +92,6 @@
   return !is_incognito_ && search::IsInstantNTP(web_contents_);
 }
 
-bool SearchIPCRouterPolicyImpl::ShouldSendToggleVoiceSearch() {
-  return true;
-}
-
 bool SearchIPCRouterPolicyImpl::ShouldSubmitQuery() {
   return true;
 }
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_impl.h b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
index b98044f..7cd10fe 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_impl.h
+++ b/chrome/browser/ui/search/search_ipc_router_policy_impl.h
@@ -21,7 +21,6 @@
   friend class SearchIPCRouterPolicyTest;
 
   // Overridden from SearchIPCRouter::Policy:
-  bool ShouldProcessSetVoiceSearchSupport() override;
   bool ShouldProcessFocusOmnibox(bool is_active_tab) override;
   bool ShouldProcessNavigateToURL(bool is_active_tab) override;
   bool ShouldProcessDeleteMostVisitedItem() override;
@@ -39,7 +38,6 @@
   bool ShouldSendOmniboxFocusChanged() override;
   bool ShouldSendMostVisitedItems() override;
   bool ShouldSendThemeBackgroundInfo() override;
-  bool ShouldSendToggleVoiceSearch() override;
   bool ShouldSubmitQuery() override;
 
   // Used by unit tests.
diff --git a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
index f358a381..ad221b5 100644
--- a/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_policy_unittest.cc
@@ -41,12 +41,6 @@
   }
 };
 
-TEST_F(SearchIPCRouterPolicyTest, ProcessVoiceSearchSupportMsg) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  EXPECT_TRUE(GetSearchIPCRouterPolicy()->
-      ShouldProcessSetVoiceSearchSupport());
-}
-
 TEST_F(SearchIPCRouterPolicyTest, ProcessFocusOmnibox) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldProcessFocusOmnibox(true));
@@ -205,7 +199,6 @@
 
   SearchIPCRouter::Policy* router_policy = GetSearchIPCRouterPolicy();
   EXPECT_TRUE(router_policy->ShouldSubmitQuery());
-  EXPECT_TRUE(router_policy->ShouldSendToggleVoiceSearch());
   EXPECT_TRUE(router_policy->ShouldSendSetOmniboxStartMargin());
 }
 
@@ -236,8 +229,3 @@
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
   EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldSubmitQuery());
 }
-
-TEST_F(SearchIPCRouterPolicyTest, SendToggleVoiceSearch) {
-  NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
-  EXPECT_TRUE(GetSearchIPCRouterPolicy()->ShouldSendToggleVoiceSearch());
-}
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index a9507a7..defeaa23 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -46,7 +46,6 @@
   virtual ~MockSearchIPCRouterDelegate() {}
 
   MOCK_METHOD1(OnInstantSupportDetermined, void(bool supports_instant));
-  MOCK_METHOD1(OnSetVoiceSearchSupport, void(bool supports_voice_search));
   MOCK_METHOD1(FocusOmnibox, void(OmniboxFocusState state));
   MOCK_METHOD3(NavigateToURL, void(const GURL&, WindowOpenDisposition, bool));
   MOCK_METHOD1(OnDeleteMostVisitedItem, void(const GURL& url));
@@ -67,7 +66,6 @@
  public:
   virtual ~MockSearchIPCRouterPolicy() {}
 
-  MOCK_METHOD0(ShouldProcessSetVoiceSearchSupport, bool());
   MOCK_METHOD1(ShouldProcessFocusOmnibox, bool(bool));
   MOCK_METHOD1(ShouldProcessNavigateToURL, bool(bool));
   MOCK_METHOD0(ShouldProcessDeleteMostVisitedItem, bool());
@@ -85,7 +83,6 @@
   MOCK_METHOD0(ShouldSendOmniboxFocusChanged, bool());
   MOCK_METHOD0(ShouldSendMostVisitedItems, bool());
   MOCK_METHOD0(ShouldSendThemeBackgroundInfo, bool());
-  MOCK_METHOD0(ShouldSendToggleVoiceSearch, bool());
   MOCK_METHOD0(ShouldSubmitQuery, bool());
 };
 
@@ -226,32 +223,6 @@
       CURRENT_TAB, true));
 }
 
-TEST_F(SearchIPCRouterTest, ProcessVoiceSearchSupportMsg) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*mock_delegate(), OnSetVoiceSearchSupport(true)).Times(1);
-  EXPECT_CALL(*(policy), ShouldProcessSetVoiceSearchSupport()).Times(1)
-      .WillOnce(testing::Return(true));
-
-  content::WebContents* contents = web_contents();
-  OnMessageReceived(ChromeViewHostMsg_SetVoiceSearchSupported(
-      contents->GetRoutingID(), GetSearchIPCRouterSeqNo(), true));
-}
-
-TEST_F(SearchIPCRouterTest, IgnoreVoiceSearchSupportMsg) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  EXPECT_CALL(*mock_delegate(), OnSetVoiceSearchSupport(true)).Times(0);
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldProcessSetVoiceSearchSupport()).Times(1)
-      .WillOnce(testing::Return(false));
-
-  content::WebContents* contents = web_contents();
-  OnMessageReceived(ChromeViewHostMsg_SetVoiceSearchSupported(
-      contents->GetRoutingID(), GetSearchIPCRouterSeqNo(), true));
-}
-
 TEST_F(SearchIPCRouterTest, ProcessFocusOmniboxMsg) {
   NavigateAndCommitActiveTab(GURL(chrome::kChromeSearchLocalNtpUrl));
   SetupMockDelegateAndPolicy();
@@ -877,30 +848,6 @@
   EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID));
 }
 
-TEST_F(SearchIPCRouterTest, SendToggleVoiceSearch) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendToggleVoiceSearch()).Times(1)
-      .WillOnce(testing::Return(true));
-
-  process()->sink().ClearMessages();
-  GetSearchIPCRouter().ToggleVoiceSearch();
-  EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxToggleVoiceSearch::ID));
-}
-
-TEST_F(SearchIPCRouterTest, DoNotSendToggleVoiceSearch) {
-  NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
-  SetupMockDelegateAndPolicy();
-  MockSearchIPCRouterPolicy* policy = GetSearchIPCRouterPolicy();
-  EXPECT_CALL(*policy, ShouldSendToggleVoiceSearch()).Times(1)
-      .WillOnce(testing::Return(false));
-
-  process()->sink().ClearMessages();
-  GetSearchIPCRouter().ToggleVoiceSearch();
-  EXPECT_FALSE(MessageWasSent(ChromeViewMsg_SearchBoxToggleVoiceSearch::ID));
-}
-
 TEST_F(SearchIPCRouterTest, SpuriousMessageTypesIgnored) {
   NavigateAndCommitActiveTab(GURL("chrome-search://foo/bar"));
   SetupMockDelegateAndPolicy();
diff --git a/chrome/browser/ui/search/search_model.cc b/chrome/browser/ui/search/search_model.cc
index 17d5a96..57b83bd 100644
--- a/chrome/browser/ui/search/search_model.cc
+++ b/chrome/browser/ui/search/search_model.cc
@@ -7,22 +7,14 @@
 #include "chrome/browser/ui/search/search_model_observer.h"
 #include "components/search/search.h"
 
-SearchModel::State::State()
-    : instant_support(INSTANT_SUPPORT_UNKNOWN),
-      voice_search_supported(false) {
-}
+SearchModel::State::State() : instant_support(INSTANT_SUPPORT_UNKNOWN) {}
 
 SearchModel::State::State(const SearchMode& mode,
-                          InstantSupportState instant_support,
-                          bool voice_search_supported)
-    : mode(mode),
-      instant_support(instant_support),
-      voice_search_supported(voice_search_supported) {
-}
+                          InstantSupportState instant_support)
+    : mode(mode), instant_support(instant_support) {}
 
 bool SearchModel::State::operator==(const State& rhs) const {
-  return mode == rhs.mode && instant_support == rhs.instant_support &&
-      voice_search_supported == rhs.voice_search_supported;
+  return mode == rhs.mode && instant_support == rhs.instant_support;
 }
 
 SearchModel::SearchModel() {
@@ -75,21 +67,6 @@
                     ModelChanged(old_state, state_));
 }
 
-void SearchModel::SetVoiceSearchSupported(bool supported) {
-  DCHECK(search::IsInstantExtendedAPIEnabled())
-      << "Please do not try to set the SearchModel state without first "
-      << "checking if Search is enabled.";
-
-  if (state_.voice_search_supported == supported)
-    return;
-
-  const State old_state = state_;
-  state_.voice_search_supported = supported;
-
-  FOR_EACH_OBSERVER(SearchModelObserver, observers_,
-                    ModelChanged(old_state, state_));
-}
-
 void SearchModel::AddObserver(SearchModelObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chrome/browser/ui/search/search_model.h b/chrome/browser/ui/search/search_model.h
index 3e97cf2..e7e701e 100644
--- a/chrome/browser/ui/search/search_model.h
+++ b/chrome/browser/ui/search/search_model.h
@@ -24,9 +24,7 @@
  public:
   struct State {
     State();
-    State(const SearchMode& mode,
-          InstantSupportState instant_support,
-          bool voice_search_supported);
+    State(const SearchMode& mode, InstantSupportState instant_support);
 
     bool operator==(const State& rhs) const;
 
@@ -35,9 +33,6 @@
 
     // Does the current page support Instant?
     InstantSupportState instant_support;
-
-    // Does the current page support voice search?
-    bool voice_search_supported;
   };
 
   SearchModel();
@@ -64,13 +59,6 @@
     return state_.instant_support;
   }
 
-  // Sets the page voice search support state.  Change notifications are sent to
-  // observers.
-  void SetVoiceSearchSupported(bool supported);
-
-  // Gets the voice search support state of the page.
-  bool voice_search_supported() const { return state_.voice_search_supported; }
-
   // Add and remove observers.
   void AddObserver(SearchModelObserver* observer);
   void RemoveObserver(SearchModelObserver* observer);
diff --git a/chrome/browser/ui/search/search_model_unittest.cc b/chrome/browser/ui/search/search_model_unittest.cc
index 646639e..565b1cd4 100644
--- a/chrome/browser/ui/search/search_model_unittest.cc
+++ b/chrome/browser/ui/search/search_model_unittest.cc
@@ -141,17 +141,3 @@
   mock_observer.VerifyNotificationCount(1);
   EXPECT_TRUE(model->state() == expected_new_state);
 }
-
-TEST_F(SearchModelTest, UpdateVoiceSearchSupported) {
-  mock_observer.VerifyNotificationCount(0);
-  EXPECT_FALSE(model->voice_search_supported());
-
-  SearchModel::State expected_old_state = model->state();
-  SearchModel::State expected_new_state(model->state());
-  expected_new_state.voice_search_supported = true;
-
-  model->SetVoiceSearchSupported(true);
-  mock_observer.VerifySearchModelStates(expected_old_state, expected_new_state);
-  mock_observer.VerifyNotificationCount(1);
-  EXPECT_TRUE(model->voice_search_supported());
-}
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 6ae2211..bcfac8f 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -269,10 +269,6 @@
   ipc_router_.OnTabDeactivated();
 }
 
-void SearchTabHelper::ToggleVoiceSearch() {
-  ipc_router_.ToggleVoiceSearch();
-}
-
 bool SearchTabHelper::IsSearchResultsPage() {
   return model_.mode().is_origin_search();
 }
@@ -371,7 +367,6 @@
   }
 
   model_.SetInstantSupportState(INSTANT_SUPPORT_UNKNOWN);
-  model_.SetVoiceSearchSupported(false);
   search::SetInstantSupportStateInNavigationEntry(model_.instant_support(),
                                                   entry);
 
@@ -383,10 +378,6 @@
   InstantSupportChanged(supports_instant);
 }
 
-void SearchTabHelper::OnSetVoiceSearchSupport(bool supports_voice_search) {
-  model_.SetVoiceSearchSupported(supports_voice_search);
-}
-
 void SearchTabHelper::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) {
   ipc_router_.SendThemeBackgroundInfo(theme_info);
 }
diff --git a/chrome/browser/ui/search/search_tab_helper.h b/chrome/browser/ui/search/search_tab_helper.h
index 0db8127..6c5bea9 100644
--- a/chrome/browser/ui/search/search_tab_helper.h
+++ b/chrome/browser/ui/search/search_tab_helper.h
@@ -90,9 +90,6 @@
   // Called when the tab corresponding to |this| instance is deactivated.
   void OnTabDeactivated();
 
-  // Tells the page to toggle voice search.
-  void ToggleVoiceSearch();
-
   // Returns true if the underlying page is a search results page.
   bool IsSearchResultsPage();
 
@@ -158,7 +155,6 @@
 
   // Overridden from SearchIPCRouter::Delegate:
   void OnInstantSupportDetermined(bool supports_instant) override;
-  void OnSetVoiceSearchSupport(bool supports_voice_search) override;
   void FocusOmnibox(OmniboxFocusState state) override;
   void NavigateToURL(const GURL& url,
                      WindowOpenDisposition disposition,
diff --git a/chrome/browser/ui/search/search_tab_helper_unittest.cc b/chrome/browser/ui/search/search_tab_helper_unittest.cc
index 678c8340..ecfc8c0 100644
--- a/chrome/browser/ui/search/search_tab_helper_unittest.cc
+++ b/chrome/browser/ui/search/search_tab_helper_unittest.cc
@@ -54,7 +54,6 @@
   virtual ~MockSearchIPCRouterDelegate() {}
 
   MOCK_METHOD1(OnInstantSupportDetermined, void(bool supports_instant));
-  MOCK_METHOD1(OnSetVoiceSearchSupport, void(bool supports_voice_search));
   MOCK_METHOD1(FocusOmnibox, void(OmniboxFocusState state));
   MOCK_METHOD3(NavigateToURL, void(const GURL&, WindowOpenDisposition, bool));
   MOCK_METHOD1(OnDeleteMostVisitedItem, void(const GURL& url));
diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h
index 17b5093..f02d601 100644
--- a/chrome/browser/ui/view_ids.h
+++ b/chrome/browser/ui/view_ids.h
@@ -88,9 +88,6 @@
   // The Download shelf.
   VIEW_ID_DOWNLOAD_SHELF,
 
-  // The omnibox icon to do voice-based search.
-  VIEW_ID_MIC_SEARCH_BUTTON,
-
   // Used in chrome/browser/ui/cocoa/view_id_util_browsertest.mm.
   // If you add new ids, make sure the above test passes.
   VIEW_ID_PREDEFINED_COUNT,
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 021a273..30c555f 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -124,8 +124,6 @@
   { ui::VKEY_F, ui::EF_ALT_DOWN, IDC_SHOW_APP_MENU},
   { ui::VKEY_E, ui::EF_ALT_DOWN, IDC_SHOW_APP_MENU},
   { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP },
-  { ui::VKEY_OEM_PERIOD, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
-    IDC_TOGGLE_SPEECH_INPUT },
   { ui::VKEY_U, ui::EF_CONTROL_DOWN, IDC_VIEW_SOURCE },
   { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
   { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 52a9952..5f8e0d2 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/autofill/save_card_icon_view.h"
 #include "chrome/browser/ui/views/browser_dialogs.h"
 #include "chrome/browser/ui/views/layout_constants.h"
@@ -74,7 +73,6 @@
 #include "grit/theme_resources.h"
 #include "ui/accessibility/ax_view_state.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/material_design/material_design_controller.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
@@ -94,7 +92,6 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/button_drag_utils.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 
@@ -140,7 +137,6 @@
       selected_keyword_view_(NULL),
       suggested_text_view_(NULL),
       keyword_hint_view_(NULL),
-      mic_search_view_(NULL),
       zoom_view_(NULL),
       open_pdf_in_reader_view_(NULL),
       manage_passwords_icon_view_(NULL),
@@ -158,9 +154,6 @@
       base::Bind(&LocationBarView::UpdateWithoutTabRestore,
                  base::Unretained(this)));
 
-  if (browser_)
-    browser_->search_model()->AddObserver(this);
-
   ui_zoom::ZoomEventManager::GetForBrowserContext(profile)
       ->AddZoomEventManagerObserver(this);
 
@@ -174,8 +167,6 @@
 LocationBarView::~LocationBarView() {
   if (template_url_service_)
     template_url_service_->RemoveObserver(this);
-  if (browser_)
-    browser_->search_model()->RemoveObserver(this);
 
   ui_zoom::ZoomEventManager::GetForBrowserContext(profile())
       ->RemoveZoomEventManagerObserver(this);
@@ -281,20 +272,6 @@
       background_color);
   AddChildView(keyword_hint_view_);
 
-  mic_search_view_ = new views::ImageButton(this);
-  mic_search_view_->set_id(VIEW_ID_MIC_SEARCH_BUTTON);
-  mic_search_view_->SetAccessibilityFocusable(true);
-  mic_search_view_->SetTooltipText(
-      l10n_util::GetStringUTF16(IDS_TOOLTIP_MIC_SEARCH));
-  mic_search_view_->SetImage(
-      views::Button::STATE_NORMAL,
-      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-          IDR_OMNIBOX_MIC_SEARCH));
-  mic_search_view_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
-                                      views::ImageButton::ALIGN_MIDDLE);
-  mic_search_view_->SetVisible(false);
-  AddChildView(mic_search_view_);
-
   const SkColor text_color = GetColor(SecurityStateModel::NONE, TEXT);
   ScopedVector<ContentSettingImageModel> models =
       ContentSettingImageModel::GenerateContentSettingImageModels();
@@ -583,12 +560,11 @@
   // Compute width of omnibox-trailing content.
   int trailing_width = horizontal_edge_thickness;
   trailing_width += IncrementalMinimumWidth(star_view_) +
-      IncrementalMinimumWidth(translate_icon_view_) +
-      IncrementalMinimumWidth(open_pdf_in_reader_view_) +
-      IncrementalMinimumWidth(save_credit_card_icon_view_) +
-      IncrementalMinimumWidth(manage_passwords_icon_view_) +
-      IncrementalMinimumWidth(zoom_view_) +
-      IncrementalMinimumWidth(mic_search_view_);
+                    IncrementalMinimumWidth(translate_icon_view_) +
+                    IncrementalMinimumWidth(open_pdf_in_reader_view_) +
+                    IncrementalMinimumWidth(save_credit_card_icon_view_) +
+                    IncrementalMinimumWidth(manage_passwords_icon_view_) +
+                    IncrementalMinimumWidth(zoom_view_);
   for (PageActionViews::const_iterator i(page_action_views_.begin());
        i != page_action_views_.end(); ++i)
     trailing_width += IncrementalMinimumWidth((*i));
@@ -711,10 +687,6 @@
                                          *i);
     }
   }
-  if (mic_search_view_->visible()) {
-    trailing_decorations.AddDecoration(vertical_padding, location_height,
-                                       mic_search_view_);
-  }
   // Because IMEs may eat the tab key, we don't show "press tab to search" while
   // IME composition is in progress.
   if (!keyword.empty() && omnibox_view_->model()->is_keyword_hint() &&
@@ -825,9 +797,6 @@
 }
 
 void LocationBarView::Update(const WebContents* contents) {
-  mic_search_view_->SetVisible(
-      !GetToolbarModel()->input_in_progress() && browser_ &&
-      browser_->search_model()->voice_search_supported());
   RefreshContentSettingViews();
   RefreshZoomView();
   RefreshPageActionViews();
@@ -1340,15 +1309,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// LocationBarView, private views::ButtonListener implementation:
-
-void LocationBarView::ButtonPressed(views::Button* sender,
-                                    const ui::Event& event) {
-  DCHECK_EQ(mic_search_view_, sender);
-  command_updater()->ExecuteCommand(IDC_TOGGLE_SPEECH_INPUT);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // LocationBarView, private views::DragController implementation:
 
 void LocationBarView::WriteDragDataForView(views::View* sender,
@@ -1440,16 +1400,3 @@
   if (omnibox_view_ && omnibox_view_->GetWidget()->IsActive())
     ShowFirstRunBubble();
 }
-
-////////////////////////////////////////////////////////////////////////////////
-// LocationBarView, private SearchModelObserver implementation:
-
-void LocationBarView::ModelChanged(const SearchModel::State& old_state,
-                                   const SearchModel::State& new_state) {
-  const bool visible = !GetToolbarModel()->input_in_progress() &&
-      new_state.voice_search_supported;
-  if (mic_search_view_->visible() != visible) {
-    mic_search_view_->SetVisible(visible);
-    Layout();
-  }
-}
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index ebd57552..0b17ae7 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -14,7 +14,6 @@
 #include "chrome/browser/ssl/security_state_model.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.h"
-#include "chrome/browser/ui/search/search_model_observer.h"
 #include "chrome/browser/ui/toolbar/chrome_toolbar_model.h"
 #include "chrome/browser/ui/views/dropdown_bar_host.h"
 #include "chrome/browser/ui/views/dropdown_bar_host_delegate.h"
@@ -26,7 +25,6 @@
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/drag_controller.h"
 
 class ActionBoxButtonView;
@@ -56,8 +54,6 @@
 
 namespace views {
 class BubbleDelegateView;
-class ImageButton;
-class ImageView;
 class Label;
 class Widget;
 }
@@ -73,13 +69,11 @@
 class LocationBarView : public LocationBar,
                         public LocationBarTesting,
                         public views::View,
-                        public views::ButtonListener,
                         public views::DragController,
                         public gfx::AnimationDelegate,
                         public ChromeOmniboxEditController,
                         public DropdownBarHostDelegate,
                         public TemplateURLServiceObserver,
-                        public SearchModelObserver,
                         public ui_zoom::ZoomEventManagerObserver {
  public:
   // The location bar view's class name.
@@ -373,9 +367,6 @@
   void OnPaint(gfx::Canvas* canvas) override;
   void PaintChildren(const ui::PaintContext& context) override;
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // views::DragController:
   void WriteDragDataForView(View* sender,
                             const gfx::Point& press_pt,
@@ -401,10 +392,6 @@
   // TemplateURLServiceObserver:
   void OnTemplateURLServiceChanged() override;
 
-  // SearchModelObserver:
-  void ModelChanged(const SearchModel::State& old_state,
-                    const SearchModel::State& new_state) override;
-
   // The Browser this LocationBarView is in.  Note that at least
   // chromeos::SimpleWebViewDialog uses a LocationBarView outside any browser
   // window, so this may be NULL.
@@ -446,9 +433,6 @@
   // Shown if the selected url has a corresponding keyword.
   KeywordHintView* keyword_hint_view_;
 
-  // The voice search icon.
-  views::ImageButton* mic_search_view_;
-
   // The content setting views.
   ContentSettingViews content_setting_views_;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 46e9ac2..0ab0325d 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -38,6 +38,7 @@
 #include "ui/gfx/render_text.h"
 #include "ui/gfx/text_utils.h"
 #include "ui/gfx/vector_icons_public.h"
+#include "ui/native_theme/native_theme.h"
 
 using ui::NativeTheme;
 
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
index b8f18545..623879c1 100644
--- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
@@ -253,8 +253,6 @@
     IDS_KEYBOARD_OVERLAY_TOGGLE_CHROMEVOX_SPOKEN_FEEDBACK },
   { "keyboardOverlayToggleProjectionTouchHud",
     IDS_KEYBOARD_OVERLAY_TOGGLE_PROJECTION_TOUCH_HUD },
-  { "keyboardOverlayToggleSpeechInput",
-    IDS_KEYBOARD_OVERLAY_TOGGLE_SPEECH_INPUT },
   { "keyboardOverlayUndo", IDS_KEYBOARD_OVERLAY_UNDO },
   { "keyboardOverlayViewKeyboardOverlay",
     IDS_KEYBOARD_OVERLAY_VIEW_KEYBOARD_OVERLAY },
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index d19a0955..768313c 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -58,12 +58,8 @@
   html_source->AddLocalizedString(
       "stickyKeysLabel", IDS_SETTINGS_STICKY_KEYS_LABEL);
   html_source->AddLocalizedString(
-      "stickyKeysSublabel", IDS_SETTINGS_STICKY_KEYS_SUBLABEL);
-  html_source->AddLocalizedString(
       "chromeVoxLabel", IDS_SETTINGS_CHROMEVOX_LABEL);
   html_source->AddLocalizedString(
-      "chromeVoxSublabel", IDS_SETTINGS_CHROMEVOX_SUBLABEL);
-  html_source->AddLocalizedString(
       "screenMagnifierLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL);
   html_source->AddLocalizedString(
       "tapDraggingLabel", IDS_SETTINGS_TAP_DRAGGING_LABEL);
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 9606818..d08c0b22 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -1078,8 +1078,6 @@
       'browser/ui/cocoa/location_bar/location_icon_decoration.mm',
       'browser/ui/cocoa/location_bar/manage_passwords_decoration.h',
       'browser/ui/cocoa/location_bar/manage_passwords_decoration.mm',
-      'browser/ui/cocoa/location_bar/mic_search_decoration.h',
-      'browser/ui/cocoa/location_bar/mic_search_decoration.mm',
       'browser/ui/cocoa/location_bar/page_action_decoration.h',
       'browser/ui/cocoa/location_bar/page_action_decoration.mm',
       'browser/ui/cocoa/location_bar/selected_keyword_decoration.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fc6b75b..bbf0c97c 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -3152,6 +3152,7 @@
             'chrome_java',
             '../base/base.gyp:base_java',
             '../base/base.gyp:base_java_test_support',
+            '../components/components.gyp:policy_java_test_support',
             '../content/content_shell_and_tests.gyp:content_java_test_support',
             '../net/net.gyp:net_java',
             '../net/net.gyp:net_java_test_support',
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 2943ccb..c93b41d 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -249,8 +249,6 @@
                     base::string16 /* identity */,
                     bool /* identity_match */)
 
-IPC_MESSAGE_ROUTED0(ChromeViewMsg_SearchBoxToggleVoiceSearch)
-
 // Sent on process startup to indicate whether this process is running in
 // incognito mode.
 IPC_MESSAGE_CONTROL1(ChromeViewMsg_SetIsIncognitoProcess,
@@ -539,11 +537,6 @@
                     int /* page_seq_no */,
                     GURL /* url */)
 
-// Tells InstantExtended whether the page supports voice search.
-IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_SetVoiceSearchSupported,
-                    int /* page_seq_no */,
-                    bool /* supported */)
-
 // Tells the renderer a list of URLs which should be bounced back to the browser
 // process so that they can be assigned to an Instant renderer.
 IPC_MESSAGE_CONTROL2(ChromeViewMsg_SetSearchURLs,
diff --git a/chrome/renderer/resources/extensions/searchbox_api.js b/chrome/renderer/resources/extensions/searchbox_api.js
index f6e5b3e..d99fa26 100644
--- a/chrome/renderer/resources/extensions/searchbox_api.js
+++ b/chrome/renderer/resources/extensions/searchbox_api.js
@@ -24,7 +24,6 @@
       native function IsFocused();
       native function IsKeyCaptureEnabled();
       native function Paste();
-      native function SetVoiceSearchSupported();
       native function StartCapturingKeyStrokes();
       native function StopCapturingKeyStrokes();
 
@@ -69,10 +68,6 @@
         Paste(value);
       };
 
-      this.setVoiceSearchSupported = function(supported) {
-        SetVoiceSearchSupported(supported);
-      };
-
       this.startCapturingKeyStrokes = function() {
         StartCapturingKeyStrokes();
       };
@@ -86,7 +81,6 @@
       this.onmarginchange = null;
       this.onsubmit = null;
       this.onsuggestionchange = null;
-      this.ontogglevoicesearch = null;
 
       //TODO(jered): Remove this empty method when google no longer requires it.
       this.setRestrictedValue = function() {};
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index 8d84d8c..d231c81 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -338,11 +338,6 @@
       render_view()->GetRoutingID(), page_seq_no_, text));
 }
 
-void SearchBox::SetVoiceSearchSupported(bool supported) {
-  render_view()->Send(new ChromeViewHostMsg_SetVoiceSearchSupported(
-      render_view()->GetRoutingID(), page_seq_no_, supported));
-}
-
 void SearchBox::StartCapturingKeyStrokes() {
   render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
       render_view()->GetRoutingID(), page_seq_no_, OMNIBOX_FOCUS_INVISIBLE));
@@ -392,8 +387,6 @@
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
                         OnThemeChanged)
-    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxToggleVoiceSearch,
-                        OnToggleVoiceSearch)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -543,13 +536,6 @@
   }
 }
 
-void SearchBox::OnToggleVoiceSearch() {
-  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
-    extensions_v8::SearchBoxExtension::DispatchToggleVoiceSearch(
-        render_view()->GetWebView()->mainFrame());
-  }
-}
-
 GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const {
   InstantMostVisitedItem item;
   return GetMostVisitedItemWithID(item_id, &item) ? item.url : GURL();
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index a71cd1be..d59e287 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -121,9 +121,6 @@
   const ThemeBackgroundInfo& GetThemeBackgroundInfo();
   const EmbeddedSearchRequestParams& GetEmbeddedSearchRequestParams();
 
-  // Sends ChromeViewHostMsg_SetVoiceSearchSupported to the browser.
-  void SetVoiceSearchSupported(bool supported);
-
   // Sends ChromeViewHostMsg_StartCapturingKeyStrokes to the browser.
   void StartCapturingKeyStrokes();
 
@@ -167,7 +164,6 @@
   void OnSubmit(const base::string16& query,
                 const EmbeddedSearchRequestParams& params);
   void OnThemeChanged(const ThemeBackgroundInfo& theme_info);
-  void OnToggleVoiceSearch();
 
   // Returns the current zoom factor of the render view or 1 on failure.
   double GetZoom() const;
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 5ff9082..c687b06 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -404,17 +404,6 @@
     "  true;"
     "}";
 
-static const char kDispatchToggleVoiceSearchScript[] =
-    "if (window.chrome &&"
-    "    window.chrome.embeddedSearch &&"
-    "    window.chrome.embeddedSearch.searchBox &&"
-    "    window.chrome.embeddedSearch.searchBox.ontogglevoicesearch &&"
-    "    typeof window.chrome.embeddedSearch.searchBox.ontogglevoicesearch =="
-    "         'function') {"
-    "  window.chrome.embeddedSearch.searchBox.ontogglevoicesearch();"
-    "  true;"
-    "}";
-
 // ----------------------------------------------------------------------------
 
 class SearchBoxExtensionWrapper : public v8::Extension {
@@ -515,10 +504,6 @@
   // Pastes provided value or clipboard's content into the omnibox.
   static void Paste(const v8::FunctionCallbackInfo<v8::Value>& args);
 
-  // Indicates whether the page supports voice search.
-  static void SetVoiceSearchSupported(
-      const v8::FunctionCallbackInfo<v8::Value>& args);
-
   // Start capturing user key strokes.
   static void StartCapturingKeyStrokes(
       const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -627,12 +612,6 @@
   Dispatch(frame, kDispatchThemeChangeEventScript);
 }
 
-// static
-void SearchBoxExtension::DispatchToggleVoiceSearch(
-    blink::WebFrame* frame) {
-  Dispatch(frame, kDispatchToggleVoiceSearchScript);
-}
-
 SearchBoxExtensionWrapper::SearchBoxExtensionWrapper(
     const base::StringPiece& code)
     : v8::Extension(kSearchBoxExtensionName, code.data(), 0, 0, code.size()) {
@@ -692,8 +671,6 @@
     return v8::FunctionTemplate::New(isolate, NavigateContentWindow);
   if (name->Equals(v8::String::NewFromUtf8(isolate, "Paste")))
     return v8::FunctionTemplate::New(isolate, Paste);
-  if (name->Equals(v8::String::NewFromUtf8(isolate, "SetVoiceSearchSupported")))
-    return v8::FunctionTemplate::New(isolate, SetVoiceSearchSupported);
   if (name->Equals(
           v8::String::NewFromUtf8(isolate, "StartCapturingKeyStrokes")))
     return v8::FunctionTemplate::New(isolate, StartCapturingKeyStrokes);
@@ -1260,22 +1237,6 @@
 }
 
 // static
-void SearchBoxExtensionWrapper::SetVoiceSearchSupported(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  content::RenderView* render_view = GetRenderView();
-  if (!render_view) {
-    return;
-  }
-  if (!args.Length()) {
-    ThrowInvalidParameters(args);
-    return;
-  }
-
-  DVLOG(1) << render_view << " SetVoiceSearchSupported";
-  SearchBox::Get(render_view)->SetVoiceSearchSupported(args[0]->BooleanValue());
-}
-
-// static
 void SearchBoxExtensionWrapper::UndoAllMostVisitedDeletions(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   content::RenderView* render_view = GetRenderView();
diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h
index fa7ea9e..162c4c3 100644
--- a/chrome/renderer/searchbox/searchbox_extension.h
+++ b/chrome/renderer/searchbox/searchbox_extension.h
@@ -45,7 +45,6 @@
   static void DispatchSubmit(blink::WebFrame* frame);
   static void DispatchSuggestionChange(blink::WebFrame* frame);
   static void DispatchThemeChange(blink::WebFrame* frame);
-  static void DispatchToggleVoiceSearch(blink::WebFrame* frame);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(SearchBoxExtension);
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 6b59c31..1f65910 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -16,6 +16,7 @@
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/invalidation/impl:java",
     "//components/policy/android:policy_java",
+    "//components/policy/android:policy_java_test_support",
     "//components/web_contents_delegate_android:web_contents_delegate_android_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
index 16cfa52..2abc171 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/ChromeInstrumentationTestRunner.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.test.util.DisableInTabbedMode;
 import org.chromium.net.test.BaseHttpTestServer;
+import org.chromium.policy.test.annotations.Policies;
 import org.chromium.ui.base.DeviceFormFactor;
 
 import java.io.File;
@@ -214,6 +215,8 @@
         super.addTestHooks(result);
         result.addSkipCheck(new DisableInTabbedModeSkipCheck());
         result.addSkipCheck(new ChromeRestrictionSkipCheck());
+
+        result.addPreTestHook(Policies.getRegistrationHook());
     }
 
     private class ChromeRestrictionSkipCheck extends RestrictionSkipCheck {
diff --git a/chrome/test/data/instant_extended.html b/chrome/test/data/instant_extended.html
index da831f5..5dde81b 100644
--- a/chrome/test/data/instant_extended.html
+++ b/chrome/test/data/instant_extended.html
@@ -18,7 +18,6 @@
 var submitCount = 0;
 var onEscKeyPressedCalls = 0;
 var onFocusChangedCalls = 0;
-var onToggleVoiceSearchCalls = 0;
 var prefetchQuery = '';
 var isFocused = false;
 var onvisibilitycalls = 0;
@@ -115,10 +114,6 @@
   isFocused = apiHandle.isFocused;
 }
 
-function handleToggleVoiceSearch() {
-  onToggleVoiceSearchCalls++;
-}
-
 function handleSuggestionChange() {
   prefetchQuery = getApiHandle().suggestion.text;
 }
@@ -138,7 +133,6 @@
   apiHandle.onchange = handleOnChange;
   apiHandle.onkeypress = handleKeyPress;
   apiHandle.onfocuschange = handleFocusChange;
-  apiHandle.ontogglevoicesearch = handleToggleVoiceSearch;
   apiHandle.onsuggestionchange = handleSuggestionChange;
   newTabPageHandle.onmostvisitedchange = handleMostVisitedChange;
   newTabPageHandle.onthemechange = handleThemeChange;
diff --git a/chrome/test/data/instant_extended_ntp.html b/chrome/test/data/instant_extended_ntp.html
index da831f5..5dde81b 100644
--- a/chrome/test/data/instant_extended_ntp.html
+++ b/chrome/test/data/instant_extended_ntp.html
@@ -18,7 +18,6 @@
 var submitCount = 0;
 var onEscKeyPressedCalls = 0;
 var onFocusChangedCalls = 0;
-var onToggleVoiceSearchCalls = 0;
 var prefetchQuery = '';
 var isFocused = false;
 var onvisibilitycalls = 0;
@@ -115,10 +114,6 @@
   isFocused = apiHandle.isFocused;
 }
 
-function handleToggleVoiceSearch() {
-  onToggleVoiceSearchCalls++;
-}
-
 function handleSuggestionChange() {
   prefetchQuery = getApiHandle().suggestion.text;
 }
@@ -138,7 +133,6 @@
   apiHandle.onchange = handleOnChange;
   apiHandle.onkeypress = handleKeyPress;
   apiHandle.onfocuschange = handleFocusChange;
-  apiHandle.ontogglevoicesearch = handleToggleVoiceSearch;
   apiHandle.onsuggestionchange = handleSuggestionChange;
   newTabPageHandle.onmostvisitedchange = handleMostVisitedChange;
   newTabPageHandle.onthemechange = handleThemeChange;
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 708cc69..7430d6f 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -34,6 +34,7 @@
 
   /** @override */
   extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([
+    'extension_item_test.js',
     'extension_service_test.js',
     'extension_sidebar_test.js',
     '../mock_controller.js',
@@ -56,6 +57,32 @@
 
 function CrExtensionsBrowserTestWithInstalledExtension() {}
 
+TEST_F('CrExtensionsBrowserTest', 'ExtensionItemNormalStateTest', function() {
+  extension_item_tests.registerTests();
+  var testNames = extension_item_tests.testNames;
+  mocha.grep(assert(testNames.ElementVisibilityNormalState)).run();
+});
+
+TEST_F('CrExtensionsBrowserTest', 'ExtensionItemDetailStateTest', function() {
+  extension_item_tests.registerTests();
+  var testNames = extension_item_tests.testNames;
+  mocha.grep(assert(testNames.ElementVisibilityDetailState)).run();
+});
+
+TEST_F('CrExtensionsBrowserTest', 'ExtensionItemDeveloperStateTest',
+       function() {
+  extension_item_tests.registerTests();
+  var testNames = extension_item_tests.testNames;
+  mocha.grep(assert(testNames.ElementVisibilityDeveloperState)).run();
+});
+
+TEST_F('CrExtensionsBrowserTest', 'ExtensionItemClickableItemsTest',
+       function() {
+  extension_item_tests.registerTests();
+  var testNames = extension_item_tests.testNames;
+  mocha.grep(assert(testNames.ClickableItems)).run();
+});
+
 CrExtensionsBrowserTestWithInstalledExtension.prototype = {
   __proto__: CrExtensionsBrowserTest.prototype,
 
diff --git a/chrome/test/data/webui/extensions/extension_item_test.js b/chrome/test/data/webui/extensions/extension_item_test.js
new file mode 100644
index 0000000..e08c08e
--- /dev/null
+++ b/chrome/test/data/webui/extensions/extension_item_test.js
@@ -0,0 +1,246 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/** @fileoverview Suite of tests for extension-item. */
+cr.define('extension_item_tests', function() {
+  /**
+   * A mock delegate for the item, capable of testing functionality.
+   * @constructor
+   * @implements {extensions.ItemDelegate}
+   */
+  function MockDelegate() {}
+
+  MockDelegate.prototype = {
+    /**
+     * Tests clicking on an element and expected a delegate call from the
+     * item.
+     * @param {HTMLElement} element The element to click on.
+     * @param {string} callName The function expected to be called.
+     * @param {Array<?>=} opt_expectedArgs The arguments the function is
+     *     expected to be called with.
+     */
+    testClickingCalls: function(element, callName, opt_expectedArgs) {
+      var mock = new MockController();
+      var mockMethod = mock.createFunctionMock(this, callName);
+      MockMethod.prototype.addExpectation.apply(
+          mockMethod, opt_expectedArgs);
+      MockInteractions.tap(element);
+      mock.verifyMocks();
+    },
+
+    /** @override */
+    deleteItem: function(id) {},
+
+    /** @override */
+    setItemEnabled: function(id, enabled) {},
+
+    /** @override */
+    showItemDetails: function(id) {},
+
+    /** @override */
+    setItemAllowedIncognito: function(id, enabled) {},
+
+    /** @override */
+    isInDevMode: function() { return false; }
+  };
+
+  /** @type {string} The mock extension's id. */
+  var id = 'a'.repeat(32);
+
+  /** @type {string} The mock extension's base URL. */
+  var baseUrl = 'chrome-extension://' + id + '/';
+
+  /**
+   * The data used to populate the extension item.
+   * @type {ExtensionInfo}
+   */
+  var extensionData = {
+    description: 'This is an extension',
+    iconUrl: 'chrome://extension-icon/' + id + '/24/0',
+    id: id,
+    incognitoAccess: {isEnabled: true, isActive: false},
+    name: 'Wonderful Extension',
+    state: 'ENABLED',
+    type: 'EXTENSION',
+    version: '2.0',
+    views: [{url: baseUrl + 'foo.html'}, {url: baseUrl + 'bar.html'}],
+  };
+
+  /**
+   * Tests that the element's visibility matches |expectedVisible| and,
+   * optionally, has specific content.
+   * @param {HTMLElement} item The item to query for the element.
+   * @param {boolean} expectedVisible Whether the element should be
+   *     visible.
+   * @param {string} selector The selector to find the element.
+   * @param {string=} opt_expected The expected textContent value.
+   */
+  function testVisible(item, expectedVisible, selector, opt_expected) {
+    var element = item.$$(selector);
+    var elementIsVisible = !!element;
+    // Iterate through the parents of the element (up to the item's root)
+    // and check if each is visible. If one is not, then the element
+    // itself is not.
+    for (var e = element; elementIsVisible && e != item.shadowRoot;
+         e = e.parentNode) {
+      elementIsVisible = !e.hidden && e.offsetWidth > 0;
+    }
+    expectEquals(expectedVisible, elementIsVisible, selector);
+    if (expectedVisible && opt_expected && element)
+      expectEquals(opt_expected, element.textContent, selector);
+  }
+
+  // The normal elements, which should always be shown.
+  var normalElements = [
+    {selector: '#name', text: extensionData.name},
+    {selector: '#description', text: extensionData.description},
+    {selector: '#version', text: extensionData.version},
+    {selector: '#enabled'},
+    {selector: '#show-details'},
+    {selector: '#delete-button'},
+  ];
+  // The elements in the details panel, which should only be shown if
+  // the isShowingDetails bit is set.
+  var detailElements = [
+    {selector: '#allow-incognito'},
+    {selector: '#details-button'},
+  ];
+  // The developer elements, which should only be shown if in developer
+  // mode *and* showing details.
+  var devElements = [
+    {selector: '#extension-id', text: 'ID:' + extensionData.id},
+    {selector: '#inspect-views'},
+  ];
+
+  /**
+   * Tests that the elements' visibility matches the expected visibility.
+   * @param {extensions.Item} item
+   * @param {Array<Object<string>>} elements
+   * @param {boolean} visibility
+   */
+  function testElementsVisibility(item, elements, visibility) {
+    elements.forEach(function(element) {
+      testVisible(item, visibility, element.selector, element.text);
+    });
+  }
+
+  /** Tests that normal elements are visible. */
+  function testNormalElementsAreVisible(item) {
+    testElementsVisibility(item, normalElements, true);
+  }
+
+  /** Tests that normal elements are hidden. */
+  function testNormalElementsAreHidden(item) {
+    testElementsVisibility(item, normalElements, false);
+  }
+
+  /** Tests that detail elements are visible. */
+  function testDetailElementsAreVisible(item) {
+    testElementsVisibility(item, detailElements, true);
+  }
+
+  /** Tests that detail elements are hidden. */
+  function testDetailElementsAreHidden(item) {
+    testElementsVisibility(item, detailElements, false);
+  }
+
+  /** Tests that dev elements are visible. */
+  function testDeveloperElementsAreVisible(item) {
+    testElementsVisibility(item, devElements, true);
+  }
+
+  /** Tests that dev elements are hidden. */
+  function testDeveloperElementsAreHidden(item) {
+    testElementsVisibility(item, devElements, false);
+  }
+
+  var testNames = {
+    ElementVisibilityNormalState: 'element visibility: normal state',
+    ElementVisibilityDetailState:
+        'element visibility: after tapping show details',
+    ElementVisibilityDeveloperState:
+        'element visibility: after enabling developer mode',
+    ClickableItems: 'clickable items',
+  };
+
+  function registerTests() {
+    suite('ExtensionItemTest', function() {
+      /**
+       * Extension item created before each test.
+       * @type {extensions.Item}
+       */
+      var item;
+
+      /** @type {MockDelegate} */
+      var mockDelegate;
+
+      suiteSetup(function() {
+        return PolymerTest.importHtml('chrome://extensions/item.html');
+      });
+
+      // Initialize an extension item before each test.
+      setup(function() {
+        PolymerTest.clearBody();
+        mockDelegate = new MockDelegate();
+        item = new extensions.Item(extensionData, mockDelegate);
+        document.body.appendChild(item);
+      });
+
+      test(testNames.ElementVisibilityNormalState, function() {
+        testNormalElementsAreVisible(item);
+        testDetailElementsAreHidden(item);
+        testDeveloperElementsAreHidden(item);
+
+        expectTrue(item.$.enabled.checked);
+        expectEquals('Enabled', item.$.enabled.textContent);
+        item.set('data.state', 'DISABLED');
+        expectFalse(item.$.enabled.checked);
+        expectEquals('Disabled', item.$.enabled.textContent);
+      });
+
+      test(testNames.ElementVisibilityDetailState, function() {
+        MockInteractions.tap(item.$['show-details']);
+        testNormalElementsAreVisible(item);
+        testDetailElementsAreVisible(item);
+        testDeveloperElementsAreHidden(item);
+      });
+
+      test(testNames.ElementVisibilitydevState, function() {
+        MockInteractions.tap(item.$['show-details']);
+        item.set('inDevMode', true);
+
+        testNormalElementsAreVisible(item);
+        testDetailElementsAreVisible(item);
+        testDeveloperElementsAreVisible(item);
+
+        // Toggling "show details" should also hide the developer elements.
+        MockInteractions.tap(item.$['show-details']);
+        testNormalElementsAreVisible(item);
+        testDetailElementsAreHidden(item);
+        testDeveloperElementsAreHidden(item);
+      });
+
+      /** Tests that the delegate methods are correctly called. */
+      test(testNames.ClickableItems, function() {
+        MockInteractions.tap(item.$['show-details']);
+        item.set('inDevMode', true);
+
+        mockDelegate.testClickingCalls(
+            item.$['delete-button'], 'deleteItem', [item.data.id]);
+        mockDelegate.testClickingCalls(
+            item.$.enabled, 'setItemEnabled', [item.data.id, false]);
+        mockDelegate.testClickingCalls(
+            item.$$('#allow-incognito'), 'setItemAllowedIncognito',
+            [item.data.id, true]);
+        mockDelegate.testClickingCalls(
+            item.$$('#details-button'), 'showItemDetails', [item.data.id]);
+      });
+    });
+  }
+
+  return {
+    registerTests: registerTests,
+    testNames: testNames,
+  };
+});
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 3814746..845e52c 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -617,7 +617,6 @@
 
 PasswordAutofillAgent::PasswordAutofillAgent(content::RenderFrame* render_frame)
     : content::RenderFrameObserver(render_frame),
-      legacy_(render_frame->GetRenderView(), this),
       logging_state_active_(false),
       was_username_autofilled_(false),
       was_password_autofilled_(false),
@@ -1188,14 +1187,14 @@
   }
 }
 
-void PasswordAutofillAgent::LegacyDidStartProvisionalLoad(
-    blink::WebLocalFrame* navigated_frame) {
+void PasswordAutofillAgent::DidStartProvisionalLoad() {
   scoped_ptr<RendererSavePasswordProgressLogger> logger;
   if (logging_state_active_) {
     logger.reset(new RendererSavePasswordProgressLogger(this, routing_id()));
     logger->LogMessage(Logger::STRING_DID_START_PROVISIONAL_LOAD_METHOD);
   }
 
+  const blink::WebLocalFrame* navigated_frame = render_frame()->GetWebFrame();
   if (navigated_frame->parent()) {
     if (logger)
       logger->LogMessage(Logger::STRING_FRAME_NOT_MAIN_FRAME);
@@ -1511,25 +1510,4 @@
          provisionally_saved_form_->new_password_value.empty());
 }
 
-// LegacyPasswordAutofillAgent -------------------------------------------------
-
-PasswordAutofillAgent::LegacyPasswordAutofillAgent::LegacyPasswordAutofillAgent(
-    content::RenderView* render_view,
-    PasswordAutofillAgent* agent)
-    : content::RenderViewObserver(render_view), agent_(agent) {
-}
-
-PasswordAutofillAgent::LegacyPasswordAutofillAgent::
-    ~LegacyPasswordAutofillAgent() {
-}
-
-void PasswordAutofillAgent::LegacyPasswordAutofillAgent::OnDestruct() {
-  // No op. Do not delete |this|.
-}
-
-void PasswordAutofillAgent::LegacyPasswordAutofillAgent::
-    DidStartProvisionalLoad(blink::WebLocalFrame* navigated_frame) {
-  agent_->LegacyDidStartProvisionalLoad(navigated_frame);
-}
-
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 69f2bb5..6db7c2d7 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -140,40 +140,18 @@
     DISALLOW_COPY_AND_ASSIGN(PasswordValueGatekeeper);
   };
 
-  // Thunk class for RenderViewObserver methods that haven't yet been migrated
-  // to RenderFrameObserver. Should eventually be removed.
-  // http://crbug.com/433486
-  class LegacyPasswordAutofillAgent : public content::RenderViewObserver {
-   public:
-    LegacyPasswordAutofillAgent(content::RenderView* render_view,
-                                PasswordAutofillAgent* agent);
-    ~LegacyPasswordAutofillAgent() override;
-
-    // RenderViewObserver:
-    void OnDestruct() override;
-    void DidStartProvisionalLoad(blink::WebLocalFrame* frame) override;
-
-   private:
-    PasswordAutofillAgent* agent_;
-
-    DISALLOW_COPY_AND_ASSIGN(LegacyPasswordAutofillAgent);
-  };
-  friend class LegacyPasswordAutofillAgent;
-
   // RenderFrameObserver:
   bool OnMessageReceived(const IPC::Message& message) override;
   void DidFinishDocumentLoad() override;
   void DidFinishLoad() override;
   void FrameDetached() override;
   void FrameWillClose() override;
+  void DidStartProvisionalLoad() override;
   void DidCommitProvisionalLoad(bool is_new_navigation,
                                 bool is_same_page_navigation) override;
   void WillSendSubmitEvent(const blink::WebFormElement& form) override;
   void WillSubmitForm(const blink::WebFormElement& form) override;
 
-  // Legacy RenderViewObserver:
-  void LegacyDidStartProvisionalLoad(blink::WebLocalFrame* frame);
-
   // RenderView IPC handlers:
   void OnFillPasswordForm(int key, const PasswordFormFillData& form_data);
   void OnSetLoggingState(bool active);
@@ -233,9 +211,6 @@
   // Helper function called when in-page navigation completed
   void OnSamePageNavigationCompleted();
 
-  // Passes through |RenderViewObserver| method to |this|.
-  LegacyPasswordAutofillAgent legacy_;
-
   // The logins we have filled so far with their associated info.
   WebInputToPasswordInfoMap web_input_to_password_info_;
   // And the keys under which PasswordAutofillManager can find the same info.
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 198e0765..f9975f03 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -1567,17 +1567,12 @@
           'type': 'none',
           'dependencies': [
             'components.gyp:invalidation_java',
+            'components.gyp:policy_java',
+            'components.gyp:policy_java_test_support',
             '../base/base.gyp:base_java',
             '../base/base.gyp:base_java_test_support',
             '../testing/android/junit/junit_test.gyp:junit_test_support',
           ],
-          'conditions': [
-            ['configuration_policy == 1', {
-              'dependencies': [
-                'components.gyp:policy_java',
-              ],
-            }],
-          ],
           'variables': {
             'main_class': 'org.chromium.testing.local.JunitTestMain',
             'src_paths': [
diff --git a/components/mus/public/cpp/lib/window.cc b/components/mus/public/cpp/lib/window.cc
index 3fede87..ded9b52 100644
--- a/components/mus/public/cpp/lib/window.cc
+++ b/components/mus/public/cpp/lib/window.cc
@@ -175,6 +175,10 @@
   return window->connection() && window->connection()->GetRoot() == window;
 }
 
+bool OwnsWindowOrIsRoot(Window* window) {
+  return OwnsWindow(window->connection(), window) || IsConnectionRoot(window);
+}
+
 void EmptyEmbedCallback(bool result, ConnectionSpecificId connection_id) {}
 
 }  // namespace
@@ -183,7 +187,7 @@
 // Window, public:
 
 void Window::Destroy() {
-  if (!OwnsWindow(connection_, this))
+  if (!OwnsWindowOrIsRoot(this))
     return;
 
   if (connection_)
@@ -203,9 +207,7 @@
 }
 
 void Window::SetBounds(const gfx::Rect& bounds) {
-  const bool is_root = !parent();
-  const bool can_change = OwnsWindow(connection_, this) || is_root;
-  if (!can_change)
+  if (!OwnsWindowOrIsRoot(this))
     return;
   if (bounds_ == bounds)
     return;
@@ -216,7 +218,7 @@
 }
 
 void Window::SetClientArea(const gfx::Insets& client_area) {
-  if (!OwnsWindow(connection_, this) && !IsConnectionRoot(this))
+  if (!OwnsWindowOrIsRoot(this))
     return;
 
   if (connection_) {
diff --git a/components/mus/public/cpp/window.h b/components/mus/public/cpp/window.h
index 1b68e10..b7eb8b2 100644
--- a/components/mus/public/cpp/window.h
+++ b/components/mus/public/cpp/window.h
@@ -49,10 +49,10 @@
   using EmbedCallback = base::Callback<void(bool, ConnectionSpecificId)>;
 
   // Destroys this window and all its children. Destruction is allowed for
-  // windows
-  // that were created by this connection. For windows from other connections
-  // (such as the root) Destroy() does nothing. If the destruction is allowed
-  // observers are notified and the Window is immediately deleted.
+  // windows that were created by this connection, or the root window. For
+  // windows from other connections (except the root), Destroy() does nothing.
+  // If the destruction is allowed observers are notified and the Window is
+  // immediately deleted.
   void Destroy();
 
   WindowTreeConnection* connection() { return connection_; }
diff --git a/components/mus/public/cpp/window_tree_delegate.h b/components/mus/public/cpp/window_tree_delegate.h
index 6cbf9aa6..3e2f397 100644
--- a/components/mus/public/cpp/window_tree_delegate.h
+++ b/components/mus/public/cpp/window_tree_delegate.h
@@ -22,8 +22,7 @@
 // Window.
 // WindowTreeConnection is deleted by any of the following:
 // . If the root of the connection is destroyed. This happens if the owner
-//   of the root Embed()s another app in root, or the owner explicitly deletes
-//   root.
+//   of the root Embed()s another app in root, or root is explicitly deleted.
 // . The connection to the window manager is lost.
 // . Explicitly by way of calling delete.
 //
@@ -34,16 +33,6 @@
  public:
   // Called when the application implementing this interface is embedded at
   // |root|.
-  //
-  // |services| exposes the services offered by the embedder to the delegate.
-  //
-  // |exposed_services| is an object that the delegate can add services to
-  // expose to the embedder.
-  //
-  // Note that if a different application is subsequently embedded at |root|,
-  // the pipes connecting |services| and |exposed_services| to the embedder and
-  // any services obtained from them are not broken and will continue to be
-  // valid.
   virtual void OnEmbed(Window* root) = 0;
 
   // Sent when another app is embedded in the same Window as this connection.
@@ -51,8 +40,8 @@
   // notified appropriately).
   virtual void OnUnembed();
 
-  // Called from the destructor of WindowTreeConnection after all the Views have
-  // been destroyed. |connection| is no longer valid after this call.
+  // Called from the destructor of WindowTreeConnection after all the Windows
+  // have been destroyed. |connection| is no longer valid after this call.
   virtual void OnConnectionLost(WindowTreeConnection* connection) = 0;
 
  protected:
diff --git a/components/mus/ws/window_manager_client_apptest.cc b/components/mus/ws/window_manager_client_apptest.cc
index 6105047..67ade08 100644
--- a/components/mus/ws/window_manager_client_apptest.cc
+++ b/components/mus/ws/window_manager_client_apptest.cc
@@ -381,23 +381,24 @@
   EXPECT_TRUE(window->bounds() == window_in_embedded->bounds());
 }
 
-// Verifies that a window can only be destroyed by the connection that created
-// it.
+// Verifies that a root window can always be destroyed.
 TEST_F(WindowServerTest, DestroySecurity) {
   Window* window = window_manager()->NewWindow();
   window->SetVisible(true);
   window_manager()->GetRoot()->AddChild(window);
+
   WindowTreeConnection* embedded = Embed(window).connection;
   ASSERT_NE(nullptr, embedded);
 
-  Window* window_in_embedded = embedded->GetWindowById(window->id());
-
-  WindowTracker tracker2(window_in_embedded);
-  window_in_embedded->Destroy();
-  // Window should not have been destroyed.
-  EXPECT_TRUE(tracker2.is_valid());
-
+  // The root can be destroyed, even though it was not created by the
+  // connection.
+  Window* embed_root = embedded->GetWindowById(window->id());
   WindowTracker tracker1(window);
+  WindowTracker tracker2(embed_root);
+  embed_root->Destroy();
+  EXPECT_FALSE(tracker2.is_valid());
+  EXPECT_TRUE(tracker1.is_valid());
+
   window->Destroy();
   EXPECT_FALSE(tracker1.is_valid());
 }
diff --git a/components/mus/ws/window_tree_impl.cc b/components/mus/ws/window_tree_impl.cc
index 48aaa20..56ccf6a9 100644
--- a/components/mus/ws/window_tree_impl.cc
+++ b/components/mus/ws/window_tree_impl.cc
@@ -570,7 +570,9 @@
   ServerWindow* window =
       GetWindow(WindowIdFromTransportId(transport_window_id));
   bool success = false;
-  if (window && access_policy_->CanDeleteWindow(window)) {
+  bool should_close = window && (access_policy_->CanDeleteWindow(window) ||
+                                 ShouldRouteToWindowManager(window));
+  if (should_close) {
     WindowTreeImpl* connection =
         connection_manager_->GetConnection(window->id().connection_id);
     success = connection && connection->DeleteWindowImpl(this, window);
diff --git a/components/password_manager/content/browser/content_password_manager_driver_factory.cc b/components/password_manager/content/browser/content_password_manager_driver_factory.cc
index 1fe3a46..b401017 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_factory.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_factory.cc
@@ -56,11 +56,7 @@
     CreateDriverForFrame(main_frame);
 }
 
-ContentPasswordManagerDriverFactory::~ContentPasswordManagerDriverFactory() {
-  STLDeleteContainerPairSecondPointers(frame_driver_map_.begin(),
-                                       frame_driver_map_.end());
-  frame_driver_map_.clear();
-}
+ContentPasswordManagerDriverFactory::~ContentPasswordManagerDriverFactory() {}
 
 // static
 ContentPasswordManagerDriverFactory*
@@ -87,7 +83,6 @@
 
 void ContentPasswordManagerDriverFactory::RenderFrameDeleted(
     content::RenderFrameHost* render_frame_host) {
-  delete frame_driver_map_[render_frame_host];
   frame_driver_map_.erase(render_frame_host);
 }
 
@@ -96,7 +91,8 @@
     content::RenderFrameHost* render_frame_host) {
   if (!render_frame_host->IsRenderFrameLive())
     return false;
-  return frame_driver_map_[render_frame_host]->HandleMessage(message);
+  return frame_driver_map_.find(render_frame_host)
+      ->second->HandleMessage(message);
 }
 
 void ContentPasswordManagerDriverFactory::DidNavigateAnyFrame(
@@ -105,22 +101,23 @@
     const content::FrameNavigateParams& params) {
   if (!render_frame_host->IsRenderFrameLive())
     return;
-  frame_driver_map_[render_frame_host]->DidNavigateFrame(details, params);
+  frame_driver_map_.find(render_frame_host)
+      ->second->DidNavigateFrame(details, params);
 }
 
 void ContentPasswordManagerDriverFactory::CreateDriverForFrame(
     content::RenderFrameHost* render_frame_host) {
-  DCHECK(!frame_driver_map_[render_frame_host]);
-  frame_driver_map_[render_frame_host] = new ContentPasswordManagerDriver(
-      render_frame_host, password_client_, autofill_client_);
+  DCHECK(!ContainsKey(frame_driver_map_, render_frame_host));
+  frame_driver_map_.set(
+      render_frame_host,
+      make_scoped_ptr(new ContentPasswordManagerDriver(
+          render_frame_host, password_client_, autofill_client_)));
 }
 
 void ContentPasswordManagerDriverFactory::TestingSetDriverForFrame(
     content::RenderFrameHost* render_frame_host,
     scoped_ptr<ContentPasswordManagerDriver> driver) {
-  if (frame_driver_map_[render_frame_host])
-    delete frame_driver_map_[render_frame_host];
-  frame_driver_map_[render_frame_host] = driver.release();
+  frame_driver_map_.set(render_frame_host, driver.Pass());
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/content/browser/content_password_manager_driver_factory.h b/components/password_manager/content/browser/content_password_manager_driver_factory.h
index 3724bd0..b2a5daa 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_factory.h
+++ b/components/password_manager/content/browser/content_password_manager_driver_factory.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/containers/scoped_ptr_map.h"
 #include "base/supports_user_data.h"
 #include "components/password_manager/core/browser/password_autofill_manager.h"
 #include "components/password_manager/core/browser/password_generation_manager.h"
@@ -65,7 +66,8 @@
 
   void CreateDriverForFrame(content::RenderFrameHost* render_frame_host);
 
-  std::map<content::RenderFrameHost*, ContentPasswordManagerDriver*>
+  base::ScopedPtrMap<content::RenderFrameHost*,
+                     scoped_ptr<ContentPasswordManagerDriver>>
       frame_driver_map_;
 
   PasswordManagerClient* password_client_;
diff --git a/components/password_manager/core/browser/credential_manager_password_form_manager.cc b/components/password_manager/core/browser/credential_manager_password_form_manager.cc
index f0ae126..1547977 100644
--- a/components/password_manager/core/browser/credential_manager_password_form_manager.cc
+++ b/components/password_manager/core/browser/credential_manager_password_form_manager.cc
@@ -23,7 +23,7 @@
                           observed_form,
                           true),
       delegate_(delegate) {
-  FetchMatchingLoginsFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
+  FetchDataFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
 }
 
 CredentialManagerPasswordFormManager::~CredentialManagerPasswordFormManager() {
diff --git a/components/password_manager/core/browser/mock_password_store.cc b/components/password_manager/core/browser/mock_password_store.cc
index 5ede681..b6b65fc 100644
--- a/components/password_manager/core/browser/mock_password_store.cc
+++ b/components/password_manager/core/browser/mock_password_store.cc
@@ -14,4 +14,12 @@
 MockPasswordStore::~MockPasswordStore() {
 }
 
+ScopedVector<InteractionsStats> MockPasswordStore::GetSiteStatsImpl(
+    const GURL& origin_domain) {
+  std::vector<InteractionsStats*> stats = GetSiteStatsMock(origin_domain);
+  ScopedVector<InteractionsStats> result;
+  result.swap(stats);
+  return result.Pass();
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index 1127a3c..a12b8e3 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -7,6 +7,7 @@
 
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace password_manager {
@@ -47,12 +48,12 @@
   MOCK_METHOD1(FillBlacklistLogins,
                bool(ScopedVector<autofill::PasswordForm>*));
   MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&));
-  void AddSiteStatsImpl(const InteractionsStats& stats) override {}
-  void RemoveSiteStatsImpl(const GURL& origin_domain) override {}
+  // GMock doesn't allow to return noncopyable types.
   ScopedVector<InteractionsStats> GetSiteStatsImpl(
-      const GURL& origin_domain) override {
-    return ScopedVector<InteractionsStats>();
-  }
+      const GURL& origin_domain) override;
+  MOCK_METHOD1(GetSiteStatsMock, std::vector<InteractionsStats*>(const GURL&));
+  MOCK_METHOD1(AddSiteStatsImpl, void(const InteractionsStats&));
+  MOCK_METHOD1(RemoveSiteStatsImpl, void(const GURL&));
 
   PasswordStoreSync* GetSyncInterface() { return this; }
 
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 562bf54a..04c3bc2 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -22,6 +22,7 @@
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
 using autofill::FormStructure;
@@ -298,7 +299,7 @@
   UpdateLogin();
 }
 
-void PasswordFormManager::FetchMatchingLoginsFromPasswordStore(
+void PasswordFormManager::FetchDataFromPasswordStore(
     PasswordStore::AuthorizationPromptPolicy prompt_policy) {
   if (state_ == MATCHING_PHASE) {
     // There is currently a password store query in progress. Remember the
@@ -326,6 +327,13 @@
     return;
   }
   password_store->GetLogins(observed_form_, prompt_policy, this);
+
+// The statistics isn't needed on mobile, only on desktop. Let's save some
+// processor cycles.
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  // The statistics is needed for the "Save password?" bubble.
+  password_store->GetSiteStats(observed_form_.origin.GetOrigin(), this);
+#endif
 }
 
 bool PasswordFormManager::HasCompletedMatching() const {
@@ -570,7 +578,7 @@
   if (next_prompt_policy_) {
     // The received results are no longer up-to-date, need to re-request.
     state_ = PRE_MATCHING_PHASE;
-    FetchMatchingLoginsFromPasswordStore(*next_prompt_policy_);
+    FetchDataFromPasswordStore(*next_prompt_policy_);
     next_prompt_policy_.reset();
     return;
   }
@@ -599,6 +607,14 @@
   }
 }
 
+void PasswordFormManager::OnGetSiteStatistics(
+    ScopedVector<InteractionsStats> stats) {
+  // On Windows the password request may be resolved after the statistics due to
+  // importing from IE.
+  DCHECK(state_ == MATCHING_PHASE || state_ == POST_MATCHING_PHASE) << state_;
+  interactions_stats_.swap(stats);
+}
+
 void PasswordFormManager::SaveAsNewLogin() {
   DCHECK_EQ(state_, POST_MATCHING_PHASE);
   DCHECK(IsNewLogin());
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index 0f36410..c07975e 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -74,11 +74,12 @@
   // they match. The return value is a MatchResultMask bitmask.
   MatchResultMask DoesManage(const autofill::PasswordForm& form) const;
 
-  // Retrieves potential matching logins from the database.
+  // Retrieves potential matching logins from the database. In addition the
+  // statistics is retrived on platforms with the password bubble.
   // |prompt_policy| indicates whether it's permissible to prompt the user to
   // authorize access to locked passwords. This argument is only used on
   // platforms that support prompting the user for access (such as Mac OS).
-  void FetchMatchingLoginsFromPasswordStore(
+  void FetchDataFromPasswordStore(
       PasswordStore::AuthorizationPromptPolicy prompt_policy);
 
   // Simple state-check to verify whether this object as received a callback
@@ -119,8 +120,10 @@
   // delayed until the data arrives.
   void ProcessFrame(const base::WeakPtr<PasswordManagerDriver>& driver);
 
+  // PasswordStoreConsumer:
   void OnGetPasswordStoreResults(
       ScopedVector<autofill::PasswordForm> results) override;
+  void OnGetSiteStatistics(ScopedVector<InteractionsStats> stats) override;
 
   // A user opted to 'never remember' passwords for this form.
   // Blacklist it so that from now on when it is seen we ignore it.
@@ -205,6 +208,12 @@
     // Just need to update the internal states.
     state_ = MATCHING_PHASE;
   }
+
+  // TODO(vasilii): remove the unit test restriction when it's needed in
+  // production code.
+  const std::vector<InteractionsStats*>& interactions_stats() const {
+    return interactions_stats_.get();
+  }
 #endif
 
   const autofill::PasswordForm& observed_form() const { return observed_form_; }
@@ -420,6 +429,9 @@
   // The PasswordForm from the page or dialog managed by |this|.
   const autofill::PasswordForm observed_form_;
 
+  // Statistics for the current domain.
+  ScopedVector<InteractionsStats> interactions_stats_;
+
   // Stores provisionally saved form until |pending_credentials_| is created.
   scoped_ptr<const autofill::PasswordForm> provisionally_saved_form_;
   // Stores if for creating |pending_credentials_| other possible usernames
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index c9aa020..02a4339 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -6,6 +6,7 @@
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/testing_pref_service.h"
+#include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/histogram_tester.h"
 #include "components/autofill/core/browser/autofill_manager.h"
@@ -22,6 +23,7 @@
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
@@ -298,6 +300,8 @@
     saved_match_.form_data.fields.push_back(field);
 
     mock_store_ = new NiceMock<MockPasswordStore>();
+    ON_CALL(*mock_store_, GetSiteStatsMock(_))
+        .WillByDefault(Return(std::vector<InteractionsStats*>()));
     client_.reset(new TestPasswordManagerClient(mock_store_.get()));
     password_manager_.reset(new PasswordManager(client_.get()));
     form_manager_.reset(new PasswordFormManager(
@@ -317,7 +321,7 @@
     const PasswordStore::AuthorizationPromptPolicy auth_policy =
         PasswordStore::DISALLOW_PROMPT;
     EXPECT_CALL(*mock_store(), GetLogins(p->observed_form(), auth_policy, p));
-    p->FetchMatchingLoginsFromPasswordStore(auth_policy);
+    p->FetchDataFromPasswordStore(auth_policy);
     if (result == RESULT_NO_MATCH) {
       p->OnGetPasswordStoreResults(ScopedVector<PasswordForm>());
       return;
@@ -1240,7 +1244,7 @@
       PasswordStore::DISALLOW_PROMPT;
   EXPECT_CALL(*mock_store(),
               GetLogins(encountered_form, auth_policy, &form_manager));
-  form_manager.FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager.FetchDataFromPasswordStore(auth_policy);
 
   // Password store only has these incomplete credentials.
   scoped_ptr<PasswordForm> incomplete_form(new PasswordForm());
@@ -1665,7 +1669,7 @@
   const PasswordStore::AuthorizationPromptPolicy auth_policy =
       PasswordStore::DISALLOW_PROMPT;
   EXPECT_CALL(*mock_store(), GetLogins(*form, auth_policy, &form_manager));
-  form_manager.FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager.FetchDataFromPasswordStore(auth_policy);
 
   // Suddenly, the frame and its driver disappear.
   client()->KillDriver();
@@ -1681,7 +1685,7 @@
       PasswordStore::DISALLOW_PROMPT;
   EXPECT_CALL(*mock_store(),
               GetLogins(*observed_form(), auth_policy, form_manager()));
-  form_manager()->FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
 
   ScopedVector<PasswordForm> simulated_results;
   scoped_ptr<PasswordForm> form(new PasswordForm(*observed_form()));
@@ -1961,8 +1965,7 @@
 
   // Do not notify the store observer after this GetLogins call.
   EXPECT_CALL(*mock_store(), GetLogins(_, _, _));
-  form_manager.FetchMatchingLoginsFromPasswordStore(
-      PasswordStore::DISALLOW_PROMPT);
+  form_manager.FetchDataFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
 
   PasswordForm submitted_form(form);
   submitted_form.password_value += ASCIIToUTF16("add stuff, make it different");
@@ -1995,8 +1998,7 @@
   EXPECT_CALL(*mock_store(), GetLogins(_, _, _))
       .WillOnce(testing::WithArg<2>(
           InvokeConsumer(form, form_related, form_related2, form_unrelated)));
-  form_manager.FetchMatchingLoginsFromPasswordStore(
-      PasswordStore::DISALLOW_PROMPT);
+  form_manager.FetchDataFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
 
   form_manager.ProvisionallySave(
       form, PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
@@ -2026,8 +2028,7 @@
   EXPECT_CALL(*mock_store(), GetLogins(_, _, _))
       .WillOnce(testing::WithArg<2>(
           InvokeConsumer(form, form_related, form_related2, form_unrelated)));
-  form_manager.FetchMatchingLoginsFromPasswordStore(
-      PasswordStore::DISALLOW_PROMPT);
+  form_manager.FetchDataFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
 
   PasswordForm submitted_form(form);
   submitted_form.password_value += ASCIIToUTF16("add stuff, make it different");
@@ -2184,7 +2185,7 @@
       PasswordStore::DISALLOW_PROMPT;
   EXPECT_CALL(*mock_store(),
               GetLogins(*observed_form(), auth_policy, form_manager()));
-  form_manager()->FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
 
   scoped_ptr<PasswordForm> generated_form(new PasswordForm(*observed_form()));
   generated_form->type = PasswordForm::TYPE_GENERATED;
@@ -2217,7 +2218,7 @@
       PasswordStore::DISALLOW_PROMPT;
   EXPECT_CALL(*mock_store(),
               GetLogins(*observed_form(), auth_policy, form_manager()));
-  form_manager()->FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
 
   scoped_ptr<PasswordForm> generated_form(new PasswordForm(*observed_form()));
   generated_form->type = PasswordForm::TYPE_GENERATED;
@@ -2251,8 +2252,8 @@
   EXPECT_CALL(*mock_store(), GetLogins(form_manager()->observed_form(),
                                        auth_policy, form_manager()))
       .Times(2);
-  form_manager()->FetchMatchingLoginsFromPasswordStore(auth_policy);
-  form_manager()->FetchMatchingLoginsFromPasswordStore(auth_policy);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
 
   // First response from the store, should be ignored.
   scoped_ptr<PasswordForm> saved_form(new PasswordForm(*saved_match()));
@@ -2301,8 +2302,7 @@
   // Ask store for logins, but store should not respond yet.
   EXPECT_CALL(*mock_store(),
               GetLogins(form_manager()->observed_form(), _, form_manager()));
-  form_manager()->FetchMatchingLoginsFromPasswordStore(
-      PasswordStore::DISALLOW_PROMPT);
+  form_manager()->FetchDataFromPasswordStore(PasswordStore::DISALLOW_PROMPT);
 
   // Now add the extra driver.
   form_manager()->ProcessFrame(extra_driver.AsWeakPtr());
@@ -2509,4 +2509,36 @@
   EXPECT_EQ(credentials.origin, new_credentials.origin);
 }
 
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+TEST_F(PasswordFormManagerTest, FetchStatistics) {
+  const PasswordStore::AuthorizationPromptPolicy auth_policy =
+      PasswordStore::DISALLOW_PROMPT;
+  InteractionsStats stats;
+  stats.origin_domain = observed_form()->origin.GetOrigin();
+  stats.username_value = saved_match()->username_value;
+  stats.dismissal_count = 5;
+  EXPECT_CALL(*mock_store(),
+              GetLogins(*observed_form(), auth_policy, form_manager()));
+  std::vector<InteractionsStats*> db_stats;
+  db_stats.push_back(new InteractionsStats(stats));
+  EXPECT_CALL(*mock_store(), GetSiteStatsMock(stats.origin_domain))
+      .WillOnce(Return(db_stats));
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(form_manager()->interactions_stats(),
+              ElementsAre(Pointee(stats)));
+}
+#else
+TEST_F(PasswordFormManagerTest, DontFetchStatistics) {
+  const PasswordStore::AuthorizationPromptPolicy auth_policy =
+      PasswordStore::DISALLOW_PROMPT;
+  EXPECT_CALL(*mock_store(),
+              GetLogins(*observed_form(), auth_policy, form_manager()));
+  EXPECT_CALL(*mock_store(), GetSiteStatsMock(_)).Times(0);
+  form_manager()->FetchDataFromPasswordStore(auth_policy);
+  base::RunLoop().RunUntilIdle();
+}
+#endif
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index b609e6f..99be508 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -340,7 +340,7 @@
 
 void PasswordManager::UpdateFormManagers() {
   for (PasswordFormManager* form_manager : pending_login_managers_) {
-    form_manager->FetchMatchingLoginsFromPasswordStore(
+    form_manager->FetchDataFromPasswordStore(
         client_->GetAuthorizationPromptPolicy(form_manager->observed_form()));
   }
 }
@@ -514,7 +514,7 @@
     PasswordStore::AuthorizationPromptPolicy prompt_policy =
         client_->GetAuthorizationPromptPolicy(*iter);
 
-    manager->FetchMatchingLoginsFromPasswordStore(prompt_policy);
+    manager->FetchDataFromPasswordStore(prompt_policy);
   }
 
   if (logger) {
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index c1a3e6f08..6fb28b50 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/password_manager/core/browser/password_autofill_manager.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
@@ -101,7 +102,7 @@
   arg0->OnGetPasswordStoreResults(result.Pass());
 }
 
-ACTION(InvokeEmptyConsumer) {
+ACTION(InvokeEmptyConsumerWithForms) {
   arg0->OnGetPasswordStoreResults(ScopedVector<PasswordForm>());
 }
 
@@ -118,6 +119,7 @@
 
     EXPECT_CALL(client_, GetPasswordStore())
         .WillRepeatedly(Return(store_.get()));
+    EXPECT_CALL(*store_, GetSiteStatsMock(_)).Times(AnyNumber());
     EXPECT_CALL(client_, GetDriver()).WillRepeatedly(Return(&driver_));
 
     manager_.reset(new PasswordManager(&client_));
@@ -235,7 +237,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -273,7 +275,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -345,7 +347,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -363,7 +365,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -402,7 +404,7 @@
   std::vector<PasswordForm> observed;
   observed.push_back(first_form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   observed.clear();
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -445,7 +447,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -518,7 +520,7 @@
   PasswordForm login_form(MakeTwitterLoginForm());
   observed.push_back(login_form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -545,7 +547,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -668,7 +670,7 @@
   std::vector<PasswordForm> observed;
   observed.push_back(first_form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -701,7 +703,7 @@
   form.new_password_value.clear();
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillOnce(WithArg<2>(InvokeEmptyConsumer()));
+      .WillOnce(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -736,7 +738,7 @@
   PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillOnce(WithArg<2>(InvokeEmptyConsumer()));
+      .WillOnce(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -789,7 +791,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillOnce(WithArg<2>(InvokeEmptyConsumer()));
+      .WillOnce(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -851,7 +853,7 @@
   observed.push_back(wrong_action_form);
 
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -911,7 +913,7 @@
   observed.push_back(wrong_submit_form);
 
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -959,7 +961,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1042,7 +1044,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1077,7 +1079,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1118,7 +1120,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1150,7 +1152,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1183,7 +1185,7 @@
   PasswordForm form(MakeFormWithOnlyNewPasswordField());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1219,7 +1221,7 @@
   std::vector<PasswordForm> observed;
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1246,7 +1248,7 @@
   std::vector<PasswordForm> observed;
   observed.push_back(empty_password_form);
   EXPECT_CALL(*store_, GetLogins(_, _, _))
-      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumer()));
+      .WillRepeatedly(WithArg<2>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 6a2c1877..b6c291c 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -15,6 +15,7 @@
 #include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/password_syncable_service.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 #include "url/origin.h"
 
 using autofill::PasswordForm;
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index ec22f90..785efc1 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -15,7 +15,6 @@
 #include "base/time/time.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
-#include "components/password_manager/core/browser/statistics_table.h"
 #include "sync/api/syncable_service.h"
 
 namespace url {
@@ -37,6 +36,7 @@
 class AffiliatedMatchHelper;
 class PasswordStoreConsumer;
 class PasswordSyncableService;
+struct InteractionsStats;
 
 // Interface for storing form passwords in a platform-specific secure way.
 // The login request/manipulation API is not threadsafe and must be used
@@ -282,9 +282,8 @@
   // Synchronous implementation for manipulating with statistics.
   virtual void AddSiteStatsImpl(const InteractionsStats& stats) = 0;
   virtual void RemoveSiteStatsImpl(const GURL& origin_domain) = 0;
-  // Returns a raw pointer so that InteractionsStats can be forward declared.
   virtual ScopedVector<InteractionsStats> GetSiteStatsImpl(
-      const GURL& origin_domain) WARN_UNUSED_RESULT = 0;
+      const GURL& origin_domain) = 0;
 
   // Log UMA stats for number of bulk deletions.
   void LogStatsForBulkDeletion(int num_deletions);
diff --git a/components/password_manager/core/browser/statistics_table.cc b/components/password_manager/core/browser/statistics_table.cc
index 6cc4027..e679663 100644
--- a/components/password_manager/core/browser/statistics_table.cc
+++ b/components/password_manager/core/browser/statistics_table.cc
@@ -22,6 +22,13 @@
 
 InteractionsStats::InteractionsStats() = default;
 
+bool operator==(const InteractionsStats& lhs, const InteractionsStats& rhs) {
+  return lhs.origin_domain == rhs.origin_domain &&
+         lhs.username_value == rhs.username_value &&
+         lhs.dismissal_count == rhs.dismissal_count &&
+         lhs.update_time == rhs.update_time;
+}
+
 StatisticsTable::StatisticsTable() : db_(nullptr) {
 }
 
@@ -58,6 +65,8 @@
 }
 
 bool StatisticsTable::AddRow(const InteractionsStats& stats) {
+  if (!stats.origin_domain.is_valid())
+    return false;
   sql::Statement s(db_->GetCachedStatement(
       SQL_FROM_HERE,
       "INSERT OR REPLACE INTO stats "
@@ -71,6 +80,8 @@
 }
 
 bool StatisticsTable::RemoveRow(const GURL& domain) {
+  if (!domain.is_valid())
+    return false;
   sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE,
                                            "DELETE FROM stats WHERE "
                                            "origin_domain = ? "));
@@ -79,6 +90,8 @@
 }
 
 ScopedVector<InteractionsStats> StatisticsTable::GetRows(const GURL& domain) {
+  if (!domain.is_valid())
+    return ScopedVector<InteractionsStats>();
   const char query[] =
       "SELECT origin_domain, username_value, "
       "dismissal_count, update_time FROM stats WHERE origin_domain == ?";
diff --git a/components/password_manager/core/browser/statistics_table.h b/components/password_manager/core/browser/statistics_table.h
index 9a48f1e..a4900f9 100644
--- a/components/password_manager/core/browser/statistics_table.h
+++ b/components/password_manager/core/browser/statistics_table.h
@@ -34,6 +34,8 @@
   base::Time update_time;
 };
 
+bool operator==(const InteractionsStats& lhs, const InteractionsStats& rhs);
+
 // Represents the 'stats' table in the Login Database.
 class StatisticsTable {
  public:
diff --git a/components/password_manager/core/browser/statistics_table_unittest.cc b/components/password_manager/core/browser/statistics_table_unittest.cc
index f592e0a..4736cea 100644
--- a/components/password_manager/core/browser/statistics_table_unittest.cc
+++ b/components/password_manager/core/browser/statistics_table_unittest.cc
@@ -23,13 +23,6 @@
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
 
-MATCHER_P(StatsIs, stats, "") {
-  return arg.origin_domain == stats.origin_domain &&
-         arg.username_value == stats.username_value &&
-         arg.dismissal_count == stats.dismissal_count &&
-         arg.update_time == stats.update_time;
-}
-
 class StatisticsTableTest : public testing::Test {
  protected:
   void SetUp() override {
@@ -65,7 +58,7 @@
 TEST_F(StatisticsTableTest, Sanity) {
   EXPECT_TRUE(db()->AddRow(test_data()));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(StatsIs(test_data()))));
+              ElementsAre(Pointee(test_data())));
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
 }
@@ -76,7 +69,7 @@
   ReloadDatabase();
 
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(StatsIs(test_data()))));
+              ElementsAre(Pointee(test_data())));
 }
 
 TEST_F(StatisticsTableTest, DoubleOperation) {
@@ -85,7 +78,7 @@
   EXPECT_TRUE(db()->AddRow(test_data()));
 
   EXPECT_THAT(db()->GetRows(test_data().origin_domain),
-              ElementsAre(Pointee(StatsIs(test_data()))));
+              ElementsAre(Pointee(test_data())));
 
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
@@ -99,9 +92,8 @@
 
   EXPECT_TRUE(db()->AddRow(stats1));
   EXPECT_TRUE(db()->AddRow(stats2));
-  EXPECT_THAT(
-      db()->GetRows(test_data().origin_domain),
-      UnorderedElementsAre(Pointee(StatsIs(stats1)), Pointee(StatsIs(stats2))));
+  EXPECT_THAT(db()->GetRows(test_data().origin_domain),
+              UnorderedElementsAre(Pointee(stats1), Pointee(stats2)));
   EXPECT_TRUE(db()->RemoveRow(test_data().origin_domain));
   EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
 }
@@ -116,15 +108,15 @@
   EXPECT_TRUE(db()->AddRow(stats1));
   EXPECT_TRUE(db()->AddRow(stats2));
   EXPECT_THAT(db()->GetRows(stats1.origin_domain),
-              ElementsAre(Pointee(StatsIs(stats1))));
+              ElementsAre(Pointee(stats1)));
   EXPECT_THAT(db()->GetRows(stats2.origin_domain),
-              ElementsAre(Pointee(StatsIs(stats2))));
+              ElementsAre(Pointee(stats2)));
 
   // Remove the first one only.
   EXPECT_TRUE(db()->RemoveStatsBetween(base::Time(), base::Time::FromTimeT(2)));
   EXPECT_THAT(db()->GetRows(stats1.origin_domain), IsEmpty());
   EXPECT_THAT(db()->GetRows(stats2.origin_domain),
-              ElementsAre(Pointee(StatsIs(stats2))));
+              ElementsAre(Pointee(stats2)));
 
   // Remove the second one only.
   EXPECT_TRUE(db()->RemoveStatsBetween(base::Time::FromTimeT(2), base::Time()));
@@ -132,5 +124,19 @@
   EXPECT_THAT(db()->GetRows(stats2.origin_domain), IsEmpty());
 }
 
+TEST_F(StatisticsTableTest, BadURL) {
+  test_data().origin_domain = GURL("trash");
+  EXPECT_FALSE(db()->AddRow(test_data()));
+  EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
+  EXPECT_FALSE(db()->RemoveRow(test_data().origin_domain));
+}
+
+TEST_F(StatisticsTableTest, EmptyURL) {
+  test_data().origin_domain = GURL();
+  EXPECT_FALSE(db()->AddRow(test_data()));
+  EXPECT_THAT(db()->GetRows(test_data().origin_domain), IsEmpty());
+  EXPECT_FALSE(db()->RemoveRow(test_data().origin_domain));
+}
+
 }  // namespace
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/test_password_store.cc b/components/password_manager/core/browser/test_password_store.cc
index 776a869..bb81d46c 100644
--- a/components/password_manager/core/browser/test_password_store.cc
+++ b/components/password_manager/core/browser/test_password_store.cc
@@ -6,6 +6,7 @@
 
 #include "base/thread_task_runner_handle.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/statistics_table.h"
 
 namespace password_manager {
 
diff --git a/components/policy.gypi b/components/policy.gypi
index 7788538..63808772 100644
--- a/components/policy.gypi
+++ b/components/policy.gypi
@@ -427,6 +427,20 @@
           },
           'includes': [ '../build/java.gypi' ],
         },
+        {
+          # GN: //components/policy/android:policy_java_test_support
+          'target_name': 'policy_java_test_support',
+          'type': 'none',
+          'dependencies': [
+            '../base/base.gyp:base_java',
+            '../base/base.gyp:base_java_test_support',
+            'policy_java'
+          ],
+          'variables': {
+            'java_in_dir': 'policy/android/javatests',
+          },
+          'includes': [ '../build/java.gypi' ],
+        },
       ],
     }],
     ['OS=="win" and target_arch=="ia32" and configuration_policy==1', {
diff --git a/components/policy/android/BUILD.gn b/components/policy/android/BUILD.gn
index 53bc165..ab1003b 100644
--- a/components/policy/android/BUILD.gn
+++ b/components/policy/android/BUILD.gn
@@ -22,6 +22,20 @@
       ]
 }
 
+# GYP: //components/components.gyp:policy_test_support_java
+android_library("policy_java_test_support") {
+  testonly = true
+  deps = [
+    "//base:base_java",
+    "//base:base_java_test_support",
+    ":policy_java",
+  ]
+  java_files = [
+    "javatests/src/org/chromium/policy/test/annotations/Policies.java",
+    "javatests/src/org/chromium/policy/test/PolicyData.java",
+  ]
+}
+
 # GYP: //components/components.gyp:policy_jni_headers
 generate_jni("jni_headers") {
   visibility = [ "//components/policy/*" ]
@@ -29,13 +43,17 @@
   jni_package = "policy"
 }
 
+# GYP: //components/components_test.gyp:components_junit_tests
 junit_binary("components_policy_junit_tests") {
   java_files = [
     "junit/src/org/chromium/policy/AbstractAppRestrictionsProviderTest.java",
     "junit/src/org/chromium/policy/CombinedPolicyProviderTest.java",
+    "junit/src/org/chromium/policy/test/annotations/PoliciesTest.java",
   ]
   deps = [
-    "//base:base_java",
+    ":policy_java_test_support",
     ":policy_java",
+    "//base:base_java",
+    "//third_party/junit:hamcrest",
   ]
 }
diff --git a/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java b/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java
index 60b40bd..82b19399 100644
--- a/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java
+++ b/components/policy/android/java/src/org/chromium/policy/AbstractAppRestrictionsProvider.java
@@ -15,6 +15,7 @@
 import android.preference.PreferenceManager;
 import android.util.Base64;
 
+import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 
@@ -30,6 +31,11 @@
 public abstract class AbstractAppRestrictionsProvider extends PolicyProvider {
     private static final String PREFERENCE_KEY = "App Restrictions";
 
+    private static final String TAG = "policy";
+
+    /** {@link Bundle} holding the restrictions to be used during tests. */
+    private static Bundle sTestRestrictions = null;
+
     private final Context mContext;
     private final SharedPreferences mSharedPreferences;
     private final BroadcastReceiver mAppRestrictionsChangedReceiver = new BroadcastReceiver() {
@@ -79,6 +85,11 @@
      */
     @Override
     public void refresh() {
+        if (sTestRestrictions != null) {
+            notifySettingsAvailable(sTestRestrictions);
+            return;
+        }
+
         final Bundle cachedResult = getCachedPolicies();
         if (cachedResult != null) {
             notifySettingsAvailable(cachedResult);
@@ -169,4 +180,18 @@
     void setTaskExecutor(Executor testExecutor) {
         mExecutor = testExecutor;
     }
+
+    /**
+     * Restrictions to be used during tests. Subsequent attempts to retrieve the restrictions will
+     * return the provided bundle instead.
+     *
+     * Chrome and WebView tests are set up to use annotations for policy testing and reset the
+     * restrictions to an empty bundle if nothing is specified. To stop using a test bundle,
+     * provide {@code null} as value instead.
+     */
+    @VisibleForTesting
+    public static void setTestRestrictions(Bundle policies) {
+        Log.d(TAG, "Test Restrictions: %s", policies.keySet().toArray());
+        sTestRestrictions = policies;
+    }
 }
diff --git a/components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java b/components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java
new file mode 100644
index 0000000..6b230d86
--- /dev/null
+++ b/components/policy/android/javatests/src/org/chromium/policy/test/PolicyData.java
@@ -0,0 +1,119 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.policy.test;
+
+import android.os.Bundle;
+
+import junit.framework.Assert;
+
+import org.chromium.base.Log;
+import org.json.JSONArray;
+
+/**
+ * Helper class to transform Java types to {@link Bundle}s usable by the Policy system.
+ *
+ * Use the subclasses to define the data and then transform it using {@link #asBundle(Iterable)}
+ */
+public abstract class PolicyData {
+    private static final String TAG = "policy_test";
+    private final String mKey;
+
+    public PolicyData(String key) {
+        mKey = key;
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    public abstract void putInBundle(Bundle bundle);
+
+    public static Bundle asBundle(Iterable<PolicyData> policies) {
+        Bundle bundle = new Bundle();
+        for (PolicyData data : policies) {
+            Log.d(TAG, "Adding to policy bundle: %s", data);
+            data.putInBundle(bundle);
+        }
+        return bundle;
+    }
+
+    /** {@link PolicyData} for the {@link String} type. */
+    public static class Str extends PolicyData {
+        private final String mValue;
+
+        public Str(String key, String value) {
+            super(key);
+            mValue = value;
+        }
+
+        public String getValue() {
+            return mValue;
+        }
+
+        @Override
+        public void putInBundle(Bundle bundle) {
+            bundle.putString(getKey(), mValue);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("PolicyData.Str{%s=%s}", getKey(), mValue);
+        }
+    }
+
+    /** {@link PolicyData} with no value, for error states. Doesn't put anything in a bundle.*/
+    public static class Undefined extends PolicyData {
+        public Undefined(String key) {
+            super(key);
+        }
+
+        @Override
+        public void putInBundle(Bundle bundle) {
+            Assert.fail(String.format(
+                    "Attempted to push the '%s' policy without value to a bundle.", getKey()));
+        }
+
+        @Override
+        public String toString() {
+            return String.format("PolicyData.Undefined{%s}", getKey());
+        }
+    }
+
+    /**
+     * {@link PolicyData} for the {@link String} array type.
+     * Outputs a string encoded as a JSON array.
+     */
+    public static class StrArray extends PolicyData {
+        private final String[] mValue;
+
+        public StrArray(String key, String[] value) {
+            super(key);
+            mValue = value.clone();
+        }
+
+        public String[] getValue() {
+            return mValue.clone();
+        }
+
+        private String valueToString() {
+            // JSONArray(Object[]) requires API 19
+            JSONArray array = new JSONArray();
+            for (String s : mValue) {
+                array.put(s);
+            }
+            return array.toString();
+        }
+
+        @Override
+        public void putInBundle(Bundle bundle) {
+            bundle.putString(getKey(), valueToString());
+        }
+
+        @Override
+        public String toString() {
+            return String.format("PolicyData.StrArray{%s=%s}", getKey(), valueToString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java b/components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java
new file mode 100644
index 0000000..11b7be2d
--- /dev/null
+++ b/components/policy/android/javatests/src/org/chromium/policy/test/annotations/Policies.java
@@ -0,0 +1,141 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.policy.test.annotations;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import junit.framework.Assert;
+
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.test.BaseTestResult.PreTestHook;
+import org.chromium.policy.AbstractAppRestrictionsProvider;
+import org.chromium.policy.test.PolicyData;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Annotations and utilities for testing code dependent on policies.
+ *
+ * Usage example:
+ * <pre>
+ * @Policies.Add({
+ *     @Policies.Item(key="Foo", string="Bar"),
+ *     @Policies.Item(key="Baz", stringArray={"Baz"})
+ * })
+ * public class MyTestClass extends BaseActivityInstrumentationTestCase<ContentActivity> {
+ *
+ *     public void MyTest1() {
+ *         // Will run the Foo and Bar policies set
+ *     }
+ *
+ *     @Policies.Remove(@Policies.Item(key="Baz"))
+ *     public void MyTest2() {
+ *         // Will run with only the Foo policy set
+ *     }
+ * }
+ * </pre>
+ */
+public final class Policies {
+    /** Items declared here will be added to the list of used policies. */
+    @Inherited
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.METHOD, ElementType.TYPE})
+    public @interface Add {
+        Item[] value();
+    }
+
+    /** Items declared here will be removed from the list of used policies. */
+    @Inherited
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.METHOD, ElementType.TYPE})
+    public @interface Remove {
+        Item[] value();
+    }
+
+    /**
+     * Individual policy item. Identified by a {@link #key}, and optional data values.
+     * At most one value argument (e.g. {@link #string()}, {@link #stringArray()}) can be used. A
+     * test failure will be caused otherwise.
+     */
+    @Inherited
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.METHOD, ElementType.TYPE})
+    public @interface Item {
+        String key();
+
+        String string() default "";
+
+        String[] stringArray() default {};
+    }
+
+    /** Parses the annotations to extract usable information as {@link PolicyData} objects. */
+    private static Map<String, PolicyData> fromItems(Item[] items) {
+        Map<String, PolicyData> result = new HashMap<>();
+        for (Item item : items) {
+            PolicyData data = null;
+
+            if (!item.string().isEmpty()) {
+                Assert.assertNull("There can be at most one type of value for the policy", data);
+                data = new PolicyData.Str(item.key(), item.string());
+            }
+
+            if (item.stringArray().length != 0) {
+                Assert.assertNull("There can be at most one type of value for the policy", data);
+                data = new PolicyData.StrArray(item.key(), item.stringArray());
+            }
+
+            if (data == null) data = new PolicyData.Undefined(item.key());
+            result.put(data.getKey(), data);
+        }
+        return result;
+    }
+
+    /** @see PreTestHook */
+    public static PreTestHook getRegistrationHook() {
+        return new RegistrationHook();
+    }
+
+    @VisibleForTesting
+    static Map<String, PolicyData> getPolicies(AnnotatedElement element) {
+        AnnotatedElement parent = (element instanceof Method)
+                ? ((Method) element).getDeclaringClass()
+                : ((Class<?>) element).getSuperclass();
+        Map<String, PolicyData> flags = (parent == null)
+                ? new HashMap<String, PolicyData>()
+                : getPolicies(parent);
+
+        if (element.isAnnotationPresent(Policies.Add.class)) {
+            flags.putAll(fromItems(element.getAnnotation(Policies.Add.class).value()));
+        }
+
+        if (element.isAnnotationPresent(Policies.Remove.class)) {
+            flags.keySet().removeAll(
+                    fromItems(element.getAnnotation(Policies.Remove.class).value()).keySet());
+        }
+
+        return flags;
+    }
+
+    /**
+     * Registration hook for the {@link Policies} annotation family. Before a test, will parse
+     * the declared policies and use them as cached policies.
+     */
+    public static class RegistrationHook implements PreTestHook {
+        @Override
+        public void run(Context targetContext, Method testMethod) {
+            final Bundle policyBundle = PolicyData.asBundle(getPolicies(testMethod).values());
+            AbstractAppRestrictionsProvider.setTestRestrictions(policyBundle);
+        }
+    }
+}
diff --git a/components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java b/components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java
new file mode 100644
index 0000000..7bc08e44
--- /dev/null
+++ b/components/policy/android/junit/src/org/chromium/policy/test/annotations/PoliciesTest.java
@@ -0,0 +1,94 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.policy.test.annotations;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.chromium.policy.test.PolicyData;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.BlockJUnit4ClassRunner;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for the {@link Policies} annotations
+ */
+@RunWith(BlockJUnit4ClassRunner.class)
+public class PoliciesTest {
+    @Test
+    public void testGetPolicies() throws NoSuchMethodException {
+        Method method;
+
+        // Simple element, one annotation, no parent
+        assertThat(Policies.getPolicies(SomeClass.class).keySet(), is(makeSet("Ni")));
+
+        // Simple element, removing an annotation just has no effect
+        assertThat(Policies.getPolicies(SomeClassThatRemoves.class).isEmpty(), is(true));
+
+        // Simple element, adds and removes the same element: We process additions, then removals.
+        assertThat(Policies.getPolicies(SomeConfusedClass.class).isEmpty(), is(true));
+
+        // Annotations are inherited
+        method = SomeClass.class.getDeclaredMethod("someMethodWithoutWord");
+        assertThat(Policies.getPolicies(method).keySet(), is(makeSet("Ni")));
+
+        // Annotations add up
+        method = SomeClass.class.getDeclaredMethod("someMethod");
+        assertThat(Policies.getPolicies(method).keySet(), is(makeSet("Ni", "Neee-wom")));
+
+        // Annotations from methods are not inherited
+        method = SomeDerivedClass.class.getDeclaredMethod("someMethod");
+        assertThat(Policies.getPolicies(method).keySet(), is(makeSet("Ni")));
+
+        // Annotations are properly deduped, we get the one closest to the examined element
+        method = SomeClass.class.getDeclaredMethod("someMethodThatDuplicates");
+        Map<String, PolicyData> policies = Policies.getPolicies(method);
+        assertThat(policies.size(), is(1));
+        assertThat(policies.get("Ni"), is(PolicyData.Str.class));
+
+        // Annotations can be removed
+        method = SomeClass.class.getDeclaredMethod("someMethodThatTilRecentlyHadNi");
+        assertThat(Policies.getPolicies(method).keySet(),
+                is(makeSet("Ekke Ekke Ekke Ekke Ptangya Zoooooooom Boing Ni")));
+    }
+
+    private Set<String> makeSet(String... keys) {
+        return new HashSet<String>(Arrays.asList(keys));
+    }
+
+    @Policies.Add(@Policies.Item(key = "Ni"))
+    private static class SomeClass {
+        @SuppressWarnings("unused")
+        void someMethodWithoutWord() {}
+
+        @Policies.Add(@Policies.Item(key = "Neee-wom"))
+        void someMethod() {}
+
+        @Policies.Add(@Policies.Item(key = "Ni", string = "Makes it string, not undefined."))
+        void someMethodThatDuplicates() {}
+
+        @Policies.Remove(@Policies.Item(key = "Ni"))
+        @Policies.Add(@Policies.Item(key = "Ekke Ekke Ekke Ekke Ptangya Zoooooooom Boing Ni"))
+        void someMethodThatTilRecentlyHadNi() {}
+    }
+
+    private static class SomeDerivedClass extends SomeClass {
+        @Override
+        void someMethod() {}
+    }
+
+    @Policies.Remove(@Policies.Item(key = "Ni"))
+    private static class SomeClassThatRemoves {}
+
+    @Policies.Add(@Policies.Item(key = "Ni"))
+    @Policies.Remove(@Policies.Item(key = "Ni"))
+    private static class SomeConfusedClass {}
+}
diff --git a/components/proximity_auth/cryptauth/proto/cryptauth_api.proto b/components/proximity_auth/cryptauth/proto/cryptauth_api.proto
index 120b8f3..301355f 100644
--- a/components/proximity_auth/cryptauth/proto/cryptauth_api.proto
+++ b/components/proximity_auth/cryptauth/proto/cryptauth_api.proto
@@ -30,7 +30,7 @@
 
 enum DeviceType {
   UNKNOWN = 0;
-  ANDROID = 1;
+  ANDROIDOS = 1;
   CHROME = 2;
   IOS = 3;
   BROWSER = 4;
@@ -228,7 +228,7 @@
   optional string device_manufacturer = 31;
 
   // Used to indicate which type of device this is.
-  optional DeviceType device_type = 32 [default = ANDROID];
+  optional DeviceType device_type = 32 [default = ANDROIDOS];
 
   // Fields corresponding to screenlock type/features and hardware features
   // should be numbered in the 400 range.
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.cc b/components/signin/core/browser/gaia_cookie_manager_service.cc
index 9cf0a527..4b2d4a0b 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.cc
+++ b/components/signin/core/browser/gaia_cookie_manager_service.cc
@@ -57,6 +57,10 @@
 
 const int kMaxFetcherRetries = 8;
 
+// Name of the GAIA cookie that is being observed to detect when available
+// accounts have changed in the content-area.
+const char* kGaiaCookieName = "APISID";
+
 enum GaiaCookieRequestType {
   ADD_ACCOUNT,
   LOG_OUT_ALL_ACCOUNTS,
@@ -292,8 +296,7 @@
 
 void GaiaCookieManagerService::Init() {
   cookie_changed_subscription_ = signin_client_->AddCookieChangedCallback(
-      GaiaUrls::GetInstance()->google_url(),
-      "APISID",
+      GaiaUrls::GetInstance()->google_url(), kGaiaCookieName,
       base::Bind(&GaiaCookieManagerService::OnCookieChanged,
                  base::Unretained(this)));
 }
@@ -366,6 +369,15 @@
   }
 }
 
+void GaiaCookieManagerService::ForceOnCookieChangedProcessing() {
+  GURL google_url = GaiaUrls::GetInstance()->google_url();
+  net::CanonicalCookie cookie(google_url, kGaiaCookieName, "",
+                              google_url.host(), "", base::Time(), base::Time(),
+                              base::Time(), false, false, false,
+                              net::COOKIE_PRIORITY_DEFAULT);
+  OnCookieChanged(cookie, true);
+}
+
 void GaiaCookieManagerService::LogOutAllAccounts() {
   VLOG(1) << "GaiaCookieManagerService::LogOutAllAccounts";
 
@@ -434,7 +446,7 @@
 void GaiaCookieManagerService::OnCookieChanged(
     const net::CanonicalCookie& cookie,
     bool removed) {
-  DCHECK_EQ("APISID", cookie.Name());
+  DCHECK_EQ(kGaiaCookieName, cookie.Name());
   DCHECK_EQ(GaiaUrls::GetInstance()->google_url().host(), cookie.Domain());
   // Ignore changes to the cookie while requests are pending.  These changes
   // are caused by the service itself as it adds accounts.  A side effects is
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 5dcd109..d3cf34e 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -173,6 +173,11 @@
   // that a check which GAIA should be done can force it.
   void TriggerListAccounts();
 
+  // Forces the processing of OnCookieChanged. This is public so that callers
+  // that know the GAIA APISID cookie might have changed can inform the
+  // service. Virtual for testing.
+  virtual void ForceOnCookieChangedProcessing();
+
   // Add or remove observers of this helper.
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
diff --git a/components/signin/ios/browser/account_consistency_service.h b/components/signin/ios/browser/account_consistency_service.h
index 653cb11..af2436d 100644
--- a/components/signin/ios/browser/account_consistency_service.h
+++ b/components/signin/ios/browser/account_consistency_service.h
@@ -74,6 +74,9 @@
   // Does nothing if the cookie is not set on |domain|.
   void RemoveXChromeConnectedCookieFromDomain(const std::string& domain);
 
+  // Notifies the AccountConsistencyService that browsing data has been removed.
+  void OnBrowsingDataRemoved();
+
  private:
   friend class AccountConsistencyServiceTest;
 
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm
index 4dd1af4..7e6e1a1 100644
--- a/components/signin/ios/browser/account_consistency_service.mm
+++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -390,6 +390,19 @@
   }
 }
 
+void AccountConsistencyService::OnBrowsingDataRemoved() {
+  // X-CHROME-CONNECTED cookies have been removed, update internal state
+  // accordingly.
+  ResetWKWebView();
+  cookie_requests_.clear();
+  domains_with_cookies_.clear();
+  base::DictionaryValue dict;
+  signin_client_->GetPrefs()->Set(kDomainsWithCookiePref, dict);
+
+  // APISID cookie has been removed, notify the GCMS.
+  gaia_cookie_manager_service_->ForceOnCookieChangedProcessing();
+}
+
 void AccountConsistencyService::OnAddAccountToCookieCompleted(
     const std::string& account_id,
     const GoogleServiceAuthError& error) {
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm
index 7134713..3458982 100644
--- a/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -73,6 +73,16 @@
   MOCK_METHOD1(OnReceivedManageAccountsResponse, void(signin::GAIAServiceType));
 };
 
+// Mock GaiaCookieManagerService to catch call to ForceOnCookieChangedProcessing
+class MockGaiaCookieManagerService : public GaiaCookieManagerService {
+ public:
+  MockGaiaCookieManagerService()
+      : GaiaCookieManagerService(nullptr,
+                                 GaiaConstants::kChromeSource,
+                                 nullptr) {}
+  MOCK_METHOD0(ForceOnCookieChangedProcessing, void());
+};
+
 // TestWebState that allows control over its policy decider.
 class TestWebState : public web::TestWebState {
  public:
@@ -111,8 +121,7 @@
     HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
     SigninManagerBase::RegisterProfilePrefs(prefs_.registry());
 
-    gaia_cookie_manager_service_.reset(new GaiaCookieManagerService(
-        nullptr, GaiaConstants::kChromeSource, nullptr));
+    gaia_cookie_manager_service_.reset(new MockGaiaCookieManagerService());
     signin_client_.reset(new TestSigninClient(&prefs_));
     signin_manager_.reset(new FakeSigninManager(
         signin_client_.get(), nullptr, &account_tracker_service_, nullptr));
@@ -181,7 +190,7 @@
   scoped_ptr<AccountConsistencyService> account_consistency_service_;
   scoped_ptr<TestSigninClient> signin_client_;
   scoped_ptr<FakeSigninManager> signin_manager_;
-  scoped_ptr<GaiaCookieManagerService> gaia_cookie_manager_service_;
+  scoped_ptr<MockGaiaCookieManagerService> gaia_cookie_manager_service_;
   scoped_refptr<HostContentSettingsMap> settings_map_;
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
 };
@@ -362,3 +371,23 @@
   SignOut();
   EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
 }
+
+// Tests that domains with cookie are cleared when browsing data is removed.
+TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved) {
+  CR_TEST_REQUIRES_WK_WEB_VIEW();
+
+  AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
+  AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
+  SignIn();
+  EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
+  const base::DictionaryValue* dict =
+      prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref);
+  EXPECT_EQ(2u, dict->size());
+
+  EXPECT_CALL(*gaia_cookie_manager_service_, ForceOnCookieChangedProcessing())
+      .Times(1);
+  account_consistency_service_->OnBrowsingDataRemoved();
+  dict =
+      prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref);
+  EXPECT_EQ(0u, dict->size());
+}
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index df10f33..97b637fe 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -474,6 +474,11 @@
         BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE,
         BACKGROUND_SYNC_STATUS_OK);
 
+    if (existing_registration->IsFiring()) {
+      existing_registration->set_sync_state(
+          BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING);
+    }
+
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(
@@ -1255,9 +1260,12 @@
   }
 
   if (registration->options()->periodicity == SYNC_ONE_SHOT) {
-    if (status_code != SERVICE_WORKER_OK) {
+    if (registration->sync_state() ==
+        BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING) {
+      registration->set_sync_state(BACKGROUND_SYNC_STATE_PENDING);
+      registration->set_num_attempts(0);
+    } else if (status_code != SERVICE_WORKER_OK) {  // Sync failed
       bool can_retry = registration->num_attempts() < max_sync_attempts_;
-
       if (registration->sync_state() ==
           BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING) {
         registration->set_sync_state(can_retry
@@ -1270,11 +1278,11 @@
             clock_->Now() +
             base::TimeDelta::FromMinutes(kInitialRetryDelayInMins) *
                 pow(kRetryDelayFactor, registration->num_attempts() - 1));
-      } else {  // can't retry
+      } else {
         registration->set_sync_state(BACKGROUND_SYNC_STATE_FAILED);
         registration->RunFinishedCallbacks();
       }
-    } else {  // sync succeeded
+    } else {  // Sync succeeded
       registration->set_sync_state(BACKGROUND_SYNC_STATE_SUCCESS);
       registration->RunFinishedCallbacks();
     }
diff --git a/content/browser/background_sync/background_sync_manager_unittest.cc b/content/browser/background_sync/background_sync_manager_unittest.cc
index 387b2e9..1e3d416 100644
--- a/content/browser/background_sync/background_sync_manager_unittest.cc
+++ b/content/browser/background_sync/background_sync_manager_unittest.cc
@@ -275,13 +275,7 @@
  public:
   BackgroundSyncManagerTest()
       : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
-        network_change_notifier_(net::NetworkChangeNotifier::CreateMock()),
-        test_background_sync_manager_(nullptr),
-        counting_controller_(nullptr),
-        test_clock_(nullptr),
-        callback_status_(BACKGROUND_SYNC_STATUS_OK),
-        callback_sw_status_code_(SERVICE_WORKER_OK),
-        sync_events_called_(0) {
+        network_change_notifier_(net::NetworkChangeNotifier::CreateMock()) {
     sync_options_1_.tag = "foo";
     sync_options_1_.periodicity = SYNC_ONE_SHOT;
     sync_options_1_.network_state = NETWORK_STATE_ONLINE;
@@ -532,6 +526,29 @@
     return callback_status_ == BACKGROUND_SYNC_STATUS_OK;
   }
 
+  bool NotifyWhenFinished(
+      BackgroundSyncRegistrationHandle* registration_handle) {
+    callback_finished_called_ = false;
+    callback_finished_status_ = BACKGROUND_SYNC_STATUS_NOT_FOUND;
+    callback_finished_state_ = BACKGROUND_SYNC_STATE_FAILED;
+
+    registration_handle->NotifyWhenFinished(
+        base::Bind(&NotifyWhenFinishedCallback, &callback_finished_called_,
+                   &callback_finished_status_, &callback_finished_state_));
+    base::RunLoop().RunUntilIdle();
+
+    if (callback_finished_called_)
+      EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, callback_finished_status_);
+
+    return callback_finished_called_;
+  }
+
+  BackgroundSyncState FinishedState() {
+    EXPECT_TRUE(callback_finished_called_);
+    EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, callback_finished_status_);
+    return callback_finished_state_;
+  }
+
   bool GetRegistration(
       const BackgroundSyncRegistrationOptions& registration_options) {
     return GetRegistrationWithServiceWorkerId(sw_registration_id_1_,
@@ -637,14 +654,14 @@
 
   TestBrowserThreadBundle browser_thread_bundle_;
   scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
-  TestPowerSource* power_monitor_source_;  // owned by power_monitor_
+  TestPowerSource* power_monitor_source_ = nullptr;  // owned by power_monitor_
   scoped_ptr<base::PowerMonitor> power_monitor_;
   scoped_ptr<EmbeddedWorkerTestHelper> helper_;
   scoped_ptr<BackgroundSyncManager> background_sync_manager_;
   scoped_ptr<StoragePartitionImpl> storage_partition_impl_;
-  TestBackgroundSyncManager* test_background_sync_manager_;
+  TestBackgroundSyncManager* test_background_sync_manager_ = nullptr;
   CountingBackgroundSyncController* counting_controller_;
-  base::SimpleTestClock* test_clock_;
+  base::SimpleTestClock* test_clock_ = nullptr;
 
   int64 sw_registration_id_1_;
   int64 sw_registration_id_2_;
@@ -655,12 +672,16 @@
   BackgroundSyncRegistrationOptions sync_options_2_;
 
   // Callback values.
-  BackgroundSyncStatus callback_status_;
+  BackgroundSyncStatus callback_status_ = BACKGROUND_SYNC_STATUS_OK;
   scoped_ptr<BackgroundSyncRegistrationHandle> callback_registration_handle_;
   scoped_ptr<ScopedVector<BackgroundSyncRegistrationHandle>>
       callback_registration_handles_;
-  ServiceWorkerStatusCode callback_sw_status_code_;
-  int sync_events_called_;
+  ServiceWorkerStatusCode callback_sw_status_code_ = SERVICE_WORKER_OK;
+  bool callback_finished_called_ = false;
+  BackgroundSyncStatus callback_finished_status_ =
+      BACKGROUND_SYNC_STATUS_NOT_FOUND;
+  BackgroundSyncState callback_finished_state_ = BACKGROUND_SYNC_STATE_FAILED;
+  int sync_events_called_ = 0;
   ServiceWorkerVersion::StatusCallback sync_fired_callback_;
 };
 
@@ -1157,16 +1178,8 @@
   EXPECT_TRUE(Register(sync_options_1_));
   EXPECT_EQ(1, sync_events_called_);
 
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get()));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeEventSuccess) {
@@ -1174,22 +1187,13 @@
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
 
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Finish firing the event.
   sync_fired_callback_.Run(SERVICE_WORKER_OK);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, sync_events_called_);
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1197,15 +1201,7 @@
   InitDelayedSyncEventTest();
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Unregistering should set the state to UNREGISTERED but finished shouldn't
   // be called until the event finishes firing, at which point its state should
@@ -1216,10 +1212,7 @@
   // Finish firing the event.
   sync_fired_callback_.Run(SERVICE_WORKER_OK);
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, sync_events_called_);
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1227,15 +1220,7 @@
   InitDelayedSyncEventTest();
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Unregistering should set the state to UNREGISTERED but finished shouldn't
   // be called until the event finishes firing, at which point its state should
@@ -1247,9 +1232,7 @@
   sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, sync_events_called_);
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1259,17 +1242,8 @@
   SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE);
   EXPECT_TRUE(Register(sync_options_1_));
   EXPECT_TRUE(Unregister(callback_registration_handle_.get()));
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get()));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1277,16 +1251,7 @@
   InitDelayedSyncEventTest();
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Drop the client's handle to the registration before the event fires, ensure
   // that the finished callback is still run.
@@ -1296,9 +1261,7 @@
   sync_fired_callback_.Run(SERVICE_WORKER_OK);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, sync_events_called_);
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedAfterEventFailure) {
@@ -1307,71 +1270,77 @@
   EXPECT_TRUE(Register(sync_options_1_));
   EXPECT_EQ(1, sync_events_called_);
 
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state);
+  EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get()));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeEventFailure) {
   InitDelayedSyncEventTest();
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Finish firing the event.
   sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedAfterUnregistered) {
   EXPECT_TRUE(Register(sync_options_1_));
   EXPECT_TRUE(Unregister(callback_registration_handle_.get()));
 
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_TRUE(NotifyWhenFinished(callback_registration_handle_.get()));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, NotifyWhenFinishedBeforeUnregistered) {
   Register(sync_options_1_);
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
-
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
   EXPECT_TRUE(Unregister(callback_registration_handle_.get()));
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
+}
+
+TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptFails) {
+  InitDelayedSyncEventTest();
+  test_background_sync_manager_->set_max_sync_attempts(1);
+  RegisterAndVerifySyncEventDelayed(sync_options_1_);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
+
+  // Reregister the event mid-sync
+  EXPECT_TRUE(Register(sync_options_1_));
+
+  // The first sync attempt fails.
+  sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(callback_finished_called_);
+
+  // It should fire again since it was reregistered mid-sync.
+  EXPECT_TRUE(GetRegistration(sync_options_1_));
+  sync_fired_callback_.Run(SERVICE_WORKER_OK);
+  EXPECT_FALSE(GetRegistration(sync_options_1_));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
+}
+
+TEST_F(BackgroundSyncManagerTest, ReregisterMidSyncFirstAttemptSucceeds) {
+  InitDelayedSyncEventTest();
+  test_background_sync_manager_->set_max_sync_attempts(1);
+  RegisterAndVerifySyncEventDelayed(sync_options_1_);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
+
+  // Reregister the event mid-sync
+  EXPECT_TRUE(Register(sync_options_1_));
+
+  // The first sync event succeeds.
+  sync_fired_callback_.Run(SERVICE_WORKER_OK);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(callback_finished_called_);
+
+  // It should fire again since it was reregistered mid-sync.
+  EXPECT_TRUE(GetRegistration(sync_options_1_));
+  sync_fired_callback_.Run(SERVICE_WORKER_OK);
+  EXPECT_FALSE(GetRegistration(sync_options_1_));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1379,16 +1348,7 @@
   InitDelayedSyncEventTest();
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  // Register for notification when the sync is finished.
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_NOT_ALLOWED;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Unregister the event mid-sync.
   EXPECT_TRUE(Unregister(callback_registration_handle_.get()));
@@ -1396,10 +1356,9 @@
   // Finish firing the event.
   sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
+
   // Since there were no retry attempts left, the sync ultimately failed.
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest,
@@ -1408,16 +1367,7 @@
   test_background_sync_manager_->set_max_sync_attempts(2);
 
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  // Register for notification when the sync is finished.
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_NOT_ALLOWED;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Unregister the event mid-sync.
   EXPECT_TRUE(Unregister(callback_registration_handle_.get()));
@@ -1425,11 +1375,9 @@
   // Finish firing the event.
   sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
   // Since there was one retry attempt left, the sync didn't completely fail
   // before it was unregistered.
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, OverwritePendingRegistration) {
@@ -1450,16 +1398,8 @@
   EXPECT_EQ(POWER_STATE_AUTO,
             callback_registration_handle_->options()->power_state);
 
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  original_handle->NotifyWhenFinished(base::Bind(&NotifyWhenFinishedCallback,
-                                                 &notify_finished_called,
-                                                 &status, &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_TRUE(NotifyWhenFinished(original_handle.get()));
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
   EXPECT_EQ(0, sync_events_called_);
 }
 
@@ -1479,23 +1419,12 @@
   // Overwrite the firing registration.
   sync_options_1_.power_state = POWER_STATE_AUTO;
   EXPECT_TRUE(Register(sync_options_1_));
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  original_handle->NotifyWhenFinished(base::Bind(&NotifyWhenFinishedCallback,
-                                                 &notify_finished_called,
-                                                 &status, &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(original_handle.get()));
 
   // Successfully finish the first event.
   sync_fired_callback_.Run(SERVICE_WORKER_OK);
   base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, OverwriteFiringRegistrationWhichFails) {
@@ -1514,23 +1443,12 @@
   // Overwrite the firing registration.
   sync_options_1_.power_state = POWER_STATE_AUTO;
   EXPECT_TRUE(Register(sync_options_1_));
-
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  original_handle->NotifyWhenFinished(base::Bind(&NotifyWhenFinishedCallback,
-                                                 &notify_finished_called,
-                                                 &status, &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(original_handle.get()));
 
   // Fail the first event.
   sync_fired_callback_.Run(SERVICE_WORKER_ERROR_FAILED);
   base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATUS_OK, status);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_FAILED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, DisableWhilePendingNotifiesFinished) {
@@ -1540,24 +1458,14 @@
   // can fire.
   SetNetwork(net::NetworkChangeNotifier::CONNECTION_NONE);
   EXPECT_TRUE(Register(sync_options_1_));
-
-  // Listen for notification of completion.
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Corrupting the backend should result in the manager disabling itself on the
   // next operation. While disabling, it should finalize any pending
   // registrations.
   test_background_sync_manager_->set_corrupt_backend(true);
   EXPECT_FALSE(Register(sync_options_2_));
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_UNREGISTERED, FinishedState());
 }
 
 TEST_F(BackgroundSyncManagerTest, DisableWhileFiringNotifiesFinished) {
@@ -1565,30 +1473,20 @@
 
   // Register a one-shot that pauses mid-fire.
   RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  // Listen for notification of completion.
-  bool notify_finished_called = false;
-  BackgroundSyncStatus status = BACKGROUND_SYNC_STATUS_OK;
-  BackgroundSyncState sync_state = BACKGROUND_SYNC_STATE_SUCCESS;
-  callback_registration_handle_->NotifyWhenFinished(
-      base::Bind(&NotifyWhenFinishedCallback, &notify_finished_called, &status,
-                 &sync_state));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(NotifyWhenFinished(callback_registration_handle_.get()));
 
   // Corrupting the backend should result in the manager disabling itself on the
   // next operation. Even though the manager is disabled, the firing sync event
   // should still be able to complete successfully and notify as much.
   test_background_sync_manager_->set_corrupt_backend(true);
   EXPECT_FALSE(Register(sync_options_2_));
-  EXPECT_FALSE(notify_finished_called);
+  EXPECT_FALSE(callback_finished_called_);
   test_background_sync_manager_->set_corrupt_backend(false);
 
   // Successfully complete the firing event.
   sync_fired_callback_.Run(SERVICE_WORKER_OK);
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(notify_finished_called);
-  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, sync_state);
+  EXPECT_EQ(BACKGROUND_SYNC_STATE_SUCCESS, FinishedState());
 }
 
 // TODO(jkarlin): Change this to a periodic test as one-shots can't be power
@@ -1749,26 +1647,6 @@
   EXPECT_FALSE(GetRegistration(sync_options_1_));
 }
 
-TEST_F(BackgroundSyncManagerTest, ReregisterOneShotMidSync) {
-  InitDelayedSyncEventTest();
-
-  RegisterAndVerifySyncEventDelayed(sync_options_1_);
-
-  // Register the same sync, but don't delay it. It shouldn't run as it's
-  // already firing.
-  test_background_sync_manager_->set_one_shot_callback(
-      base::Bind(OneShotSuccessfulCallback, &sync_events_called_));
-  EXPECT_TRUE(Register(sync_options_1_));
-  EXPECT_EQ(1, sync_events_called_);
-  EXPECT_TRUE(GetRegistration(sync_options_1_));
-
-  // Finish the original event, note that the second never runs.
-  sync_fired_callback_.Run(SERVICE_WORKER_OK);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, sync_events_called_);
-  EXPECT_FALSE(GetRegistration(sync_options_1_));
-}
-
 TEST_F(BackgroundSyncManagerTest, UnregisterOneShotMidSync) {
   InitDelayedSyncEventTest();
 
diff --git a/content/browser/background_sync/background_sync_registration.cc b/content/browser/background_sync/background_sync_registration.cc
index 14bdc46a..b20e2ef7 100644
--- a/content/browser/background_sync/background_sync_registration.cc
+++ b/content/browser/background_sync/background_sync_registration.cc
@@ -51,6 +51,7 @@
   switch (sync_state_) {
     case BACKGROUND_SYNC_STATE_PENDING:
     case BACKGROUND_SYNC_STATE_FIRING:
+    case BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING:
     case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING:
       return false;
     case BACKGROUND_SYNC_STATE_FAILED:
@@ -62,15 +63,31 @@
   return false;
 }
 
+bool BackgroundSyncRegistration::IsFiring() const {
+  switch (sync_state_) {
+    case BACKGROUND_SYNC_STATE_FIRING:
+    case BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING:
+    case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING:
+      return true;
+    case BACKGROUND_SYNC_STATE_PENDING:
+    case BACKGROUND_SYNC_STATE_FAILED:
+    case BACKGROUND_SYNC_STATE_SUCCESS:
+    case BACKGROUND_SYNC_STATE_UNREGISTERED:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
 void BackgroundSyncRegistration::SetUnregisteredState() {
   DCHECK(!HasCompleted());
-  bool firing = sync_state_ == BACKGROUND_SYNC_STATE_FIRING ||
-                sync_state_ == BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING;
 
-  sync_state_ = firing ? BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING
-                       : BACKGROUND_SYNC_STATE_UNREGISTERED;
+  bool is_firing = IsFiring();
 
-  if (!firing) {
+  sync_state_ = is_firing ? BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING
+                          : BACKGROUND_SYNC_STATE_UNREGISTERED;
+
+  if (!is_firing) {
     // If the registration is currently firing then wait to run
     // RunFinishedCallbacks until after it has finished as it might
     // change state to SUCCESS first.
diff --git a/content/browser/background_sync/background_sync_registration.h b/content/browser/background_sync/background_sync_registration.h
index 7fe6dd9d..df558ce3 100644
--- a/content/browser/background_sync/background_sync_registration.h
+++ b/content/browser/background_sync/background_sync_registration.h
@@ -34,6 +34,7 @@
   void AddFinishedCallback(const StateCallback& callback);
   void RunFinishedCallbacks();
   bool HasCompleted() const;
+  bool IsFiring() const;
 
   // If the registration is currently firing, sets its state to
   // BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING. If it is firing, it sets
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index a9bdefbb..9e0d43a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1584,6 +1584,7 @@
     if (snapshot.has_tree_data) {
       AXContentTreeDataToAXTreeData(snapshot.tree_data,
                                     &dst_snapshot.tree_data);
+      dst_snapshot.has_tree_data = true;
     }
     it->second.Run(dst_snapshot);
     ax_tree_snapshot_callbacks_.erase(it);
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index dcd93c5..d0db9ce2 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -124,6 +124,10 @@
   if (delegate)
     delegate->GenerateMetadataDict(metadata_dict.get());
 
+  // Highres ticks.
+  metadata_dict->SetBoolean("highres-ticks",
+                            base::TimeTicks::IsHighResolution());
+
   return metadata_dict.Pass();
 }
 
diff --git a/content/child/background_sync/background_sync_provider.cc b/content/child/background_sync/background_sync_provider.cc
index 5dab9e4..a5d6039 100644
--- a/content/child/background_sync/background_sync_provider.cc
+++ b/content/child/background_sync/background_sync_provider.cc
@@ -393,6 +393,7 @@
       switch (state) {
         case BACKGROUND_SYNC_STATE_PENDING:
         case BACKGROUND_SYNC_STATE_FIRING:
+        case BACKGROUND_SYNC_STATE_REREGISTERED_WHILE_FIRING:
         case BACKGROUND_SYNC_STATE_UNREGISTERED_WHILE_FIRING:
           NOTREACHED();
           break;
diff --git a/content/common/background_sync_service.mojom b/content/common/background_sync_service.mojom
index 159f828..d1b0cd1 100644
--- a/content/common/background_sync_service.mojom
+++ b/content/common/background_sync_service.mojom
@@ -21,6 +21,7 @@
   PENDING,
   FIRING,
   UNREGISTERED_WHILE_FIRING,
+  REREGISTERED_WHILE_FIRING,
   FAILED,
   SUCCESS,
   UNREGISTERED
diff --git a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
index 69fdd308..19106cb 100644
--- a/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
+++ b/content/common/gpu/gpu_memory_buffer_factory_ozone_native_pixmap.cc
@@ -129,7 +129,7 @@
 
   scoped_refptr<gfx::GLImageOzoneNativePixmap> image(
       new gfx::GLImageOzoneNativePixmap(size, internalformat));
-  if (!image->Initialize(pixmap.get(), format)) {
+  if (!image->Initialize(pixmap.get())) {
     LOG(ERROR) << "Failed to create GLImage";
     return nullptr;
   }
diff --git a/content/common/gpu/media/va_surface.h b/content/common/gpu/media/va_surface.h
index c76c11f..8901fa6 100644
--- a/content/common/gpu/media/va_surface.h
+++ b/content/common/gpu/media/va_surface.h
@@ -90,6 +90,7 @@
 
   VASurface(VASurfaceID va_surface_id,
             const gfx::Size& size,
+            unsigned int format,
             const ReleaseCB& release_cb);
 
   VASurfaceID id() {
@@ -97,6 +98,7 @@
   }
 
   const gfx::Size& size() const { return size_; }
+  unsigned int format() const { return format_; }
 
  private:
   friend class base::RefCountedThreadSafe<VASurface>;
@@ -104,6 +106,7 @@
 
   const VASurfaceID va_surface_id_;
   gfx::Size size_;
+  unsigned int format_;
   ReleaseCB release_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(VASurface);
diff --git a/content/common/gpu/media/vaapi_drm_picture.cc b/content/common/gpu/media/vaapi_drm_picture.cc
index d344a1b5..dfa4124 100644
--- a/content/common/gpu/media/vaapi_drm_picture.cc
+++ b/content/common/gpu/media/vaapi_drm_picture.cc
@@ -17,6 +17,37 @@
 #include "ui/ozone/public/ozone_platform.h"
 #include "ui/ozone/public/surface_factory_ozone.h"
 
+namespace {
+// We decode video into YUV420, but for usage with GLImages we have to convert
+// to BGRX_8888.
+const gfx::BufferFormat kPictureForGLImageFormat = gfx::BufferFormat::BGRX_8888;
+
+uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) {
+  switch (fmt) {
+    case gfx::BufferFormat::BGRX_8888:
+      return VA_FOURCC_BGRX;
+    case gfx::BufferFormat::UYVY_422:
+      return VA_FOURCC_UYVY;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) {
+  switch (fmt) {
+    case gfx::BufferFormat::UYVY_422:
+      return VA_RT_FORMAT_YUV422;
+    case gfx::BufferFormat::BGRX_8888:
+      return VA_RT_FORMAT_RGB32;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+}  // namespace
+
 namespace content {
 
 VaapiDrmPicture::VaapiDrmPicture(
@@ -54,7 +85,8 @@
   // Create a VASurface out of the created buffer using the dmabuf.
   VASurfaceAttribExternalBuffers va_attrib_extbuf;
   memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf));
-  va_attrib_extbuf.pixel_format = VA_FOURCC_BGRX;
+  va_attrib_extbuf.pixel_format =
+      BufferFormatToVAFourCC(pixmap->GetBufferFormat());
   va_attrib_extbuf.width = pixmap_size.width();
   va_attrib_extbuf.height = pixmap_size.height();
   va_attrib_extbuf.data_size = pixmap_size.height() * dmabuf_pitch;
@@ -80,7 +112,8 @@
   va_attribs[1].value.value.p = &va_attrib_extbuf;
 
   scoped_refptr<VASurface> va_surface = vaapi_wrapper_->CreateUnownedSurface(
-      VA_RT_FORMAT_RGB32, pixmap_size, va_attribs);
+      BufferFormatToVARTFormat(pixmap->GetBufferFormat()), pixmap_size,
+      va_attribs);
   if (!va_surface) {
     LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap";
     return nullptr;
@@ -90,13 +123,13 @@
 }
 
 scoped_refptr<ui::NativePixmap> VaapiDrmPicture::CreateNativePixmap(
-    gfx::Size size) {
+    gfx::Size size,
+    gfx::BufferFormat format) {
   ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
   ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
 
   // Create a buffer from Ozone.
-  return factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, size,
-                                     gfx::BufferFormat::BGRX_8888,
+  return factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, size, format,
                                      gfx::BufferUsage::SCANOUT);
 }
 
@@ -104,7 +137,7 @@
   // We want to create a VASurface and an EGLImage out of the same
   // memory buffer, so we can output decoded pictures to it using
   // VAAPI and also use it to paint with GL.
-  pixmap_ = CreateNativePixmap(size());
+  pixmap_ = CreateNativePixmap(size(), kPictureForGLImageFormat);
   if (!pixmap_) {
     LOG(ERROR) << "Failed creating an Ozone NativePixmap";
     return false;
@@ -117,10 +150,10 @@
   }
 
   // Weak pointers can only bind to methods without return values,
-  // hence we cannot bind ScalePixmap here. Instead we use a
+  // hence we cannot bind ProcessPixmap here. Instead we use a
   // static function to solve this problem.
-  pixmap_->SetScalingCallback(base::Bind(&VaapiDrmPicture::CallScalePixmap,
-                                         weak_this_factory_.GetWeakPtr()));
+  pixmap_->SetProcessingCallback(base::Bind(&VaapiDrmPicture::CallProcessPixmap,
+                                            weak_this_factory_.GetWeakPtr()));
 
   if (!make_context_current_.Run())
     return false;
@@ -129,7 +162,7 @@
                                           texture_id());
   scoped_refptr<gfx::GLImageOzoneNativePixmap> image(
       new gfx::GLImageOzoneNativePixmap(size(), GL_BGRA_EXT));
-  if (!image->Initialize(pixmap_.get(), gfx::BufferFormat::BGRX_8888)) {
+  if (!image->Initialize(pixmap_.get())) {
     LOG(ERROR) << "Failed to create GLImage";
     return false;
   }
@@ -148,45 +181,51 @@
 }
 
 // static
-scoped_refptr<ui::NativePixmap> VaapiDrmPicture::CallScalePixmap(
+scoped_refptr<ui::NativePixmap> VaapiDrmPicture::CallProcessPixmap(
     base::WeakPtr<VaapiDrmPicture> weak_ptr,
-    gfx::Size new_size) {
+    gfx::Size target_size,
+    gfx::BufferFormat target_format) {
   if (!weak_ptr.get()) {
-    LOG(ERROR) << "Failed scaling NativePixmap as scaling "
+    LOG(ERROR) << "Failed processing NativePixmap as processing "
                   "unit(VaapiDrmPicture) is deleted";
     return nullptr;
   }
-  return weak_ptr->ScalePixmap(new_size);
+  return weak_ptr->ProcessPixmap(target_size, target_format);
 }
 
-scoped_refptr<ui::NativePixmap> VaapiDrmPicture::ScalePixmap(
-    gfx::Size new_size) {
-  if (!scaled_va_surface_.get() || scaled_va_surface_->size() != new_size) {
-    scaled_pixmap_ = CreateNativePixmap(new_size);
-    if (!scaled_pixmap_) {
-      LOG(ERROR) << "Failed creating an Ozone NativePixmap for scaling";
-      scaled_va_surface_ = nullptr;
+scoped_refptr<ui::NativePixmap> VaapiDrmPicture::ProcessPixmap(
+    gfx::Size target_size,
+    gfx::BufferFormat target_format) {
+  if (!processed_va_surface_.get() ||
+      processed_va_surface_->size() != target_size ||
+      processed_va_surface_->format() !=
+          BufferFormatToVARTFormat(target_format)) {
+    processed_pixmap_ = CreateNativePixmap(target_size, target_format);
+    if (!processed_pixmap_) {
+      LOG(ERROR) << "Failed creating an Ozone NativePixmap for processing";
+      processed_va_surface_ = nullptr;
       return nullptr;
     }
-    scaled_va_surface_ = CreateVASurfaceForPixmap(scaled_pixmap_, new_size);
-    if (!scaled_va_surface_) {
+    processed_va_surface_ =
+        CreateVASurfaceForPixmap(processed_pixmap_, target_size);
+    if (!processed_va_surface_) {
       LOG(ERROR) << "Failed creating VA Surface for pixmap";
-      scaled_pixmap_ = nullptr;
+      processed_pixmap_ = nullptr;
       return nullptr;
     }
   }
 
-  DCHECK(scaled_pixmap_);
+  DCHECK(processed_pixmap_);
   bool vpp_result =
-      vaapi_wrapper_->BlitSurface(va_surface_, scaled_va_surface_);
+      vaapi_wrapper_->BlitSurface(va_surface_, processed_va_surface_);
   if (!vpp_result) {
     LOG(ERROR) << "Failed scaling NativePixmap";
-    scaled_pixmap_ = nullptr;
-    scaled_va_surface_ = nullptr;
+    processed_pixmap_ = nullptr;
+    processed_va_surface_ = nullptr;
     return nullptr;
   }
 
-  return scaled_pixmap_;
+  return processed_pixmap_;
 }
 
 scoped_refptr<gl::GLImage> VaapiDrmPicture::GetImageToBind() {
diff --git a/content/common/gpu/media/vaapi_drm_picture.h b/content/common/gpu/media/vaapi_drm_picture.h
index 4ea787fb..e480c8c9 100644
--- a/content/common/gpu/media/vaapi_drm_picture.h
+++ b/content/common/gpu/media/vaapi_drm_picture.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/gpu/media/vaapi_picture.h"
+#include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace gl {
@@ -47,17 +48,21 @@
   bool AllowOverlay() const override;
 
  private:
-  // Calls ScalePixmap() if weak_ptr is not NULL.
-  static scoped_refptr<ui::NativePixmap> CallScalePixmap(
+  // Calls ProcessPixmap() if weak_ptr is not NULL.
+  static scoped_refptr<ui::NativePixmap> CallProcessPixmap(
       base::WeakPtr<VaapiDrmPicture> weak_ptr,
-      gfx::Size new_size);
-  // Use VPP to scale underlying pixmap_ to |new_size| and return the
-  // scaling result with a new pixmap.
-  scoped_refptr<ui::NativePixmap> ScalePixmap(gfx::Size new_size);
+      gfx::Size target_size,
+      gfx::BufferFormat target_format);
+  // Use VPP to process underlying pixmap_, scaling to |target_size| and
+  // converting to |target_format|.
+  scoped_refptr<ui::NativePixmap> ProcessPixmap(
+      gfx::Size target_size,
+      gfx::BufferFormat target_format);
   scoped_refptr<VASurface> CreateVASurfaceForPixmap(
       scoped_refptr<ui::NativePixmap> pixmap,
       gfx::Size pixmap_size);
-  scoped_refptr<ui::NativePixmap> CreateNativePixmap(gfx::Size size);
+  scoped_refptr<ui::NativePixmap> CreateNativePixmap(gfx::Size size,
+                                                     gfx::BufferFormat format);
 
   VaapiWrapper* vaapi_wrapper_;  // Not owned.
   base::Callback<bool(void)> make_context_current_;
@@ -65,8 +70,8 @@
   // Ozone buffer, the storage of the EGLImage and the VASurface.
   scoped_refptr<ui::NativePixmap> pixmap_;
 
-  // Ozone buffer, the storage of the scaled buffer for overlay.
-  scoped_refptr<ui::NativePixmap> scaled_pixmap_;
+  // Ozone buffer, the storage of the processed buffer for overlay.
+  scoped_refptr<ui::NativePixmap> processed_pixmap_;
 
   // EGLImage bound to the GL textures used by the VDA client.
   scoped_refptr<gl::GLImage> gl_image_;
@@ -74,8 +79,8 @@
   // VASurface used to transfer from the decoder's pixel format.
   scoped_refptr<VASurface> va_surface_;
 
-  // VaSurface used to apply scaling.
-  scoped_refptr<VASurface> scaled_va_surface_;
+  // VaSurface used to apply processing.
+  scoped_refptr<VASurface> processed_va_surface_;
 
   // The WeakPtrFactory for VaapiDrmPicture.
   base::WeakPtrFactory<VaapiDrmPicture> weak_this_factory_;
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.cc b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
index 704f7f2..97823a7 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
@@ -999,9 +999,9 @@
     return nullptr;
 
   DCHECK(!awaiting_va_surfaces_recycle_);
-  scoped_refptr<VASurface> va_surface(
-      new VASurface(available_va_surfaces_.front(), requested_pic_size_,
-                    va_surface_release_cb_));
+  scoped_refptr<VASurface> va_surface(new VASurface(
+      available_va_surfaces_.front(), requested_pic_size_,
+      vaapi_wrapper_->va_surface_format(), va_surface_release_cb_));
   available_va_surfaces_.pop_front();
 
   scoped_refptr<VaapiDecodeSurface> dec_surface =
diff --git a/content/common/gpu/media/vaapi_video_encode_accelerator.cc b/content/common/gpu/media/vaapi_video_encode_accelerator.cc
index 9c078b2..495f2f1 100644
--- a/content/common/gpu/media/vaapi_video_encode_accelerator.cc
+++ b/content/common/gpu/media/vaapi_video_encode_accelerator.cc
@@ -584,11 +584,13 @@
   }
 
   current_encode_job_->input_surface = new VASurface(
-      available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
+      available_va_surface_ids_.back(), coded_size_,
+      vaapi_wrapper_->va_surface_format(), va_surface_release_cb_);
   available_va_surface_ids_.pop_back();
 
   current_encode_job_->recon_surface = new VASurface(
-      available_va_surface_ids_.back(), coded_size_, va_surface_release_cb_);
+      available_va_surface_ids_.back(), coded_size_,
+      vaapi_wrapper_->va_surface_format(), va_surface_release_cb_);
   available_va_surface_ids_.pop_back();
 
   // Reference surfaces are needed until the job is done, but they get
diff --git a/content/common/gpu/media/vaapi_wrapper.cc b/content/common/gpu/media/vaapi_wrapper.cc
index 4f073d3a..4240f94 100644
--- a/content/common/gpu/media/vaapi_wrapper.cc
+++ b/content/common/gpu/media/vaapi_wrapper.cc
@@ -114,8 +114,12 @@
 
 VASurface::VASurface(VASurfaceID va_surface_id,
                      const gfx::Size& size,
+                     unsigned int format,
                      const ReleaseCB& release_cb)
-    : va_surface_id_(va_surface_id), size_(size), release_cb_(release_cb) {
+    : va_surface_id_(va_surface_id),
+      size_(size),
+      format_(format),
+      release_cb_(release_cb) {
   DCHECK(!release_cb_.is_null());
 }
 
@@ -124,7 +128,8 @@
 }
 
 VaapiWrapper::VaapiWrapper()
-    : va_display_(NULL),
+    : va_surface_format_(0),
+      va_display_(NULL),
       va_config_id_(VA_INVALID_ID),
       va_context_id_(VA_INVALID_ID),
       va_vpp_config_id_(VA_INVALID_ID),
@@ -511,6 +516,7 @@
 
   DCHECK(va_surfaces->empty());
   DCHECK(va_surface_ids_.empty());
+  DCHECK_EQ(va_surface_format_, 0u);
   va_surface_ids_.resize(num_surfaces);
 
   // Allocate surfaces in driver.
@@ -537,6 +543,7 @@
   }
 
   *va_surfaces = va_surface_ids_;
+  va_surface_format_ = va_format;
   return true;
 }
 
@@ -557,6 +564,7 @@
 
   va_surface_ids_.clear();
   va_context_id_ = VA_INVALID_ID;
+  va_surface_format_ = 0;
 }
 
 scoped_refptr<VASurface> VaapiWrapper::CreateUnownedSurface(
@@ -579,7 +587,7 @@
   // of the destruction order. All the surfaces will be destroyed
   // before VaapiWrapper.
   va_surface = new VASurface(
-      va_surface_id, size,
+      va_surface_id, size, va_format,
       base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this)));
 
   return va_surface;
diff --git a/content/common/gpu/media/vaapi_wrapper.h b/content/common/gpu/media/vaapi_wrapper.h
index 49e9f04..036bee4 100644
--- a/content/common/gpu/media/vaapi_wrapper.h
+++ b/content/common/gpu/media/vaapi_wrapper.h
@@ -195,6 +195,9 @@
   // Initialize static data before sandbox is enabled.
   static void PreSandboxInitialization();
 
+  // Get the created surfaces format.
+  unsigned int va_surface_format() const { return va_surface_format_; }
+
  private:
   struct ProfileInfo {
     VAProfile va_profile;
@@ -325,6 +328,9 @@
   // Allocated ids for VASurfaces.
   std::vector<VASurfaceID> va_surface_ids_;
 
+  // VA format of surfaces with va_surface_ids_.
+  unsigned int va_surface_format_;
+
   // Singleton instance of VADisplayState.
   static base::LazyInstance<VADisplayState> va_display_state_;
 
diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc
index 7140cc8c..7d107a0 100644
--- a/content/zygote/zygote_linux.cc
+++ b/content/zygote/zygote_linux.cc
@@ -5,6 +5,8 @@
 #include "content/zygote/zygote_linux.h"
 
 #include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -25,6 +27,7 @@
 #include "base/process/launch.h"
 #include "base/process/process.h"
 #include "base/process/process_handle.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "content/common/child_process_sandbox_support_impl_linux.h"
 #include "content/common/sandbox_linux/sandbox_linux.h"
@@ -84,14 +87,16 @@
 
 }  // namespace
 
-Zygote::Zygote(int sandbox_flags, ScopedVector<ZygoteForkDelegate> helpers,
+Zygote::Zygote(int sandbox_flags,
+               ScopedVector<ZygoteForkDelegate> helpers,
                const std::vector<base::ProcessHandle>& extra_children,
                const std::vector<int>& extra_fds)
     : sandbox_flags_(sandbox_flags),
       helpers_(helpers.Pass()),
       initial_uma_index_(0),
       extra_children_(extra_children),
-      extra_fds_(extra_fds) {}
+      extra_fds_(extra_fds),
+      to_reap_() {}
 
 Zygote::~Zygote() {
 }
@@ -109,6 +114,13 @@
   action.sa_handler = &SIGCHLDHandler;
   PCHECK(sigaction(SIGCHLD, &action, NULL) == 0);
 
+  // Block SIGCHLD until a child might be ready to reap.
+  sigset_t sigset;
+  sigset_t orig_sigmask;
+  PCHECK(sigemptyset(&sigset) == 0);
+  PCHECK(sigaddset(&sigset, SIGCHLD) == 0);
+  PCHECK(sigprocmask(SIG_BLOCK, &sigset, &orig_sigmask) == 0);
+
   if (UsingSUIDSandbox() || UsingNSSandbox()) {
     // Let the ZygoteHost know we are ready to go.
     // The receiving code is in content/browser/zygote_host_linux.cc.
@@ -128,10 +140,70 @@
 #endif
   }
 
+  sigset_t ppoll_sigmask = orig_sigmask;
+  PCHECK(sigdelset(&ppoll_sigmask, SIGCHLD) == 0);
+  struct pollfd pfd;
+  pfd.fd = kZygoteSocketPairFd;
+  pfd.events = POLLIN;
+
+  struct timespec timeout;
+  timeout.tv_sec = 2;
+  timeout.tv_nsec = 0;
+
   for (;;) {
-    // This function call can return multiple times, once per fork().
-    if (HandleRequestFromBrowser(kZygoteSocketPairFd))
-      return true;
+    struct timespec* timeout_ptr = nullptr;
+    if (!to_reap_.empty())
+      timeout_ptr = &timeout;
+    int rc = ppoll(&pfd, 1, timeout_ptr, &ppoll_sigmask);
+    PCHECK(rc >= 0 || errno == EINTR);
+    ReapChildren();
+
+    if (pfd.revents & POLLIN) {
+      // This function call can return multiple times, once per fork().
+      if (HandleRequestFromBrowser(kZygoteSocketPairFd)) {
+        PCHECK(sigprocmask(SIG_SETMASK, &orig_sigmask, NULL) == 0);
+        return true;
+      }
+    }
+  }
+  // The loop should not be exited unless a request was successfully processed.
+  NOTREACHED();
+  return false;
+}
+
+bool Zygote::ReapChild(const base::TimeTicks& now, ZygoteProcessInfo* child) {
+  pid_t pid = child->internal_pid;
+  pid_t r = HANDLE_EINTR(waitpid(pid, NULL, WNOHANG));
+  if (r > 0) {
+    if (r != pid) {
+      DLOG(ERROR) << "While waiting for " << pid << " to terminate, "
+                                                    "waitpid returned "
+                  << r;
+    }
+    return r == pid;
+  }
+  if ((now - child->time_of_reap_request).InSeconds() < 2) {
+    return false;
+  }
+  // If the process has been requested reaped >= 2 seconds ago, kill it.
+  if (!child->sent_sigkill) {
+    if (kill(pid, SIGKILL) != 0)
+      DPLOG(ERROR) << "Sending SIGKILL to process " << pid << " failed";
+
+    child->sent_sigkill = true;
+  }
+  return false;
+}
+
+void Zygote::ReapChildren() {
+  base::TimeTicks now = base::TimeTicks::Now();
+  std::vector<ZygoteProcessInfo>::iterator it = to_reap_.begin();
+  while (it != to_reap_.end()) {
+    if (ReapChild(now, &(*it))) {
+      it = to_reap_.erase(it);
+    } else {
+      it++;
+    }
   }
 }
 
@@ -243,23 +315,10 @@
     NOTREACHED();
     return;
   }
+  child_info.time_of_reap_request = base::TimeTicks::Now();
 
   if (!child_info.started_from_helper) {
-    // Do not call base::EnsureProcessTerminated() under ThreadSanitizer, as it
-    // spawns a separate thread which may live until the call to fork() in the
-    // zygote. As a result, ThreadSanitizer will report an error and almost
-    // disable race detection in the child process.
-    // Not calling EnsureProcessTerminated() may result in zombie processes
-    // sticking around. This will only happen during testing, so we can live
-    // with this for now.
-#if !defined(THREAD_SANITIZER)
-    // TODO(jln): this old code is completely broken. See crbug.com/274855.
-    base::EnsureProcessTerminated(base::Process(child_info.internal_pid));
-#else
-    LOG(WARNING) << "Zygote process omitting a call to "
-        << "base::EnsureProcessTerminated() for child pid " << child
-        << " under ThreadSanitizer. See http://crbug.com/274855.";
-#endif
+    to_reap_.push_back(child_info);
   } else {
     // For processes from the helper, send a GetTerminationStatus request
     // with known_dead set to true.
diff --git a/content/zygote/zygote_linux.h b/content/zygote/zygote_linux.h
index d371e12..f96b906d 100644
--- a/content/zygote/zygote_linux.h
+++ b/content/zygote/zygote_linux.h
@@ -15,6 +15,8 @@
 #include "base/posix/global_descriptors.h"
 #include "base/process/kill.h"
 #include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
 
 namespace base {
 class Pickle;
@@ -43,6 +45,10 @@
     base::ProcessHandle internal_pid;
     // Keeps track of which fork delegate helper the process was started from.
     ZygoteForkDelegate* started_from_helper;
+    // Records when the browser requested the zygote to reap this process.
+    base::TimeTicks time_of_reap_request;
+    // Notes whether the zygote has sent SIGKILL to this process.
+    bool sent_sigkill;
   };
   typedef base::SmallMap< std::map<base::ProcessHandle, ZygoteProcessInfo> >
       ZygoteProcessMap;
@@ -114,6 +120,14 @@
   bool HandleGetSandboxStatus(int fd,
                               base::PickleIterator iter);
 
+  // Attempt to reap the child process by calling waitpid, and return
+  // whether successful.  If the process has not terminated within
+  // 2 seconds of its reap request, send it SIGKILL.
+  bool ReapChild(const base::TimeTicks& now, ZygoteProcessInfo* child);
+
+  // Attempt to reap all outstanding children in |to_reap_|.
+  void ReapChildren();
+
   // The Zygote needs to keep some information about each process. Most
   // notably what the PID of the process is inside the PID namespace of
   // the Zygote and whether or not a process was started by the
@@ -136,6 +150,9 @@
   // This vector contains the FDs that must be closed before reaping the extra
   // children.
   std::vector<int> extra_fds_;
+
+  // The vector contains the child processes that need to be reaped.
+  std::vector<ZygoteProcessInfo> to_reap_;
 };
 
 }  // namespace content
diff --git a/extensions/renderer/resources/media_router_bindings.js b/extensions/renderer/resources/media_router_bindings.js
index c877fd4..13bf88b 100644
--- a/extensions/renderer/resources/media_router_bindings.js
+++ b/extensions/renderer/resources/media_router_bindings.js
@@ -98,6 +98,28 @@
   }
 
   /**
+   * Converts presentation connection state to Mojo enum value.
+   * @param {!string} state
+   * @return {!mediaRouterMojom.MediaRouter.PresentationConnectionState}
+   */
+  function presentationConnectionStateToMojo_(state) {
+    switch (state) {
+      case 'connected':
+        return
+            mediaRouterMojom.MediaRouter.PresentationConnectionState.CONNECTED;
+      case 'closed':
+        return mediaRouterMojom.MediaRouter.PresentationConnectionState.CLOSED;
+      case 'terminated':
+        return
+            mediaRouterMojom.MediaRouter.PresentationConnectionState.TERMINATED;
+      default:
+        console.error('Unknown presentation connection state: ' + state);
+        return
+            mediaRouterMojom.MediaRouter.PresentationConnectionState.TERMINATED;
+    }
+  }
+
+  /**
    * Creates a new MediaRouter.
    * Converts a route struct to its Mojo form.
    * @param {!MediaRouterService} service
@@ -265,13 +287,26 @@
 
   /**
    * Called by the provider manager when sink availability has been updated.
-   * @param {!MediaRouter.SinkAvailability} The new sink availability.
+   * @param {!mediaRouterMojom.MediaRouter.SinkAvailability} availability
+   *     The new sink availability.
    */
   MediaRouter.prototype.onSinkAvailabilityUpdated = function(availability) {
     this.service_.onSinkAvailabilityUpdated(availability);
   };
 
   /**
+   * Called by the provider manager when the state of a presentation connected
+   * to a route has changed.
+   * @param {!string} routeId
+   * @param {!string} state
+   */
+  MediaRouter.prototype.onPresentationConnectionStateChanged =
+      function(routeId, state) {
+    this.service_.onPresentationConnectionStateChanged(
+        routeId, presentationConnectionStateToMojo_(state));
+  };
+
+  /**
    * Object containing callbacks set by the provider manager.
    *
    * @constructor
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index d28fa43..ee1f6e7 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -23,6 +23,7 @@
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
 #include "ios/chrome/browser/history/web_history_service_factory.h"
+#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.h"
@@ -84,6 +85,7 @@
   IOSChromeLargeIconServiceFactory::GetInstance();
   IOSChromeFaviconLoaderFactory::GetInstance();
   IOSChromePasswordStoreFactory::GetInstance();
+  IOSChromeProfileInvalidationProviderFactory::GetInstance();
   OAuth2TokenServiceFactory::GetInstance();
   PersonalDataManagerFactory::GetInstance();
   SigninClientFactory::GetInstance();
diff --git a/ios/chrome/browser/invalidation/OWNERS b/ios/chrome/browser/invalidation/OWNERS
new file mode 100644
index 0000000..9b396653
--- /dev/null
+++ b/ios/chrome/browser/invalidation/OWNERS
@@ -0,0 +1,3 @@
+dcheng@chromium.org
+nyquist@chromium.org
+pavely@chromium.org
diff --git a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.cc b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.cc
new file mode 100644
index 0000000..70b7a8f
--- /dev/null
+++ b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.cc
@@ -0,0 +1,87 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h"
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/prefs/pref_registry.h"
+#include "components/gcm_driver/gcm_profile_service.h"
+#include "components/invalidation/impl/invalidator_storage.h"
+#include "components/invalidation/impl/profile_invalidation_provider.h"
+#include "components/invalidation/impl/ticl_invalidation_service.h"
+#include "components/invalidation/impl/ticl_profile_settings_provider.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/signin/core/browser/profile_identity_provider.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.h"
+#include "ios/chrome/browser/signin/oauth2_token_service_factory.h"
+#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/web/public/web_client.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using invalidation::InvalidatorStorage;
+using invalidation::ProfileInvalidationProvider;
+using invalidation::TiclInvalidationService;
+
+// static
+invalidation::ProfileInvalidationProvider*
+IOSChromeProfileInvalidationProviderFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<ProfileInvalidationProvider*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+// static
+IOSChromeProfileInvalidationProviderFactory*
+IOSChromeProfileInvalidationProviderFactory::GetInstance() {
+  return base::Singleton<IOSChromeProfileInvalidationProviderFactory>::get();
+}
+
+IOSChromeProfileInvalidationProviderFactory::
+    IOSChromeProfileInvalidationProviderFactory()
+    : BrowserStateKeyedServiceFactory(
+          "InvalidationService",
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ios::SigninManagerFactory::GetInstance());
+  DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
+  DependsOn(OAuth2TokenServiceFactory::GetInstance());
+}
+
+IOSChromeProfileInvalidationProviderFactory::
+    ~IOSChromeProfileInvalidationProviderFactory() {}
+
+scoped_ptr<KeyedService>
+IOSChromeProfileInvalidationProviderFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+
+  scoped_ptr<IdentityProvider> identity_provider(new ProfileIdentityProvider(
+      ios::SigninManagerFactory::GetForBrowserState(browser_state),
+      OAuth2TokenServiceFactory::GetForBrowserState(browser_state),
+      // LoginUIServiceFactory is not built on iOS.
+      base::Closure()));
+
+  scoped_ptr<TiclInvalidationService> service(new TiclInvalidationService(
+      web::GetWebClient()->GetUserAgent(false), identity_provider.Pass(),
+      make_scoped_ptr(new invalidation::TiclProfileSettingsProvider(
+          browser_state->GetPrefs())),
+      IOSChromeGCMProfileServiceFactory::GetForBrowserState(browser_state)
+          ->driver(),
+      browser_state->GetRequestContext()));
+  service->Init(
+      make_scoped_ptr(new InvalidatorStorage(browser_state->GetPrefs())));
+
+  return make_scoped_ptr(new ProfileInvalidationProvider(service.Pass()));
+}
+
+void IOSChromeProfileInvalidationProviderFactory::RegisterBrowserStatePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  ProfileInvalidationProvider::RegisterProfilePrefs(registry);
+  InvalidatorStorage::RegisterProfilePrefs(registry);
+}
diff --git a/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h
new file mode 100644
index 0000000..5fc2bf3
--- /dev/null
+++ b/ios/chrome/browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h
@@ -0,0 +1,56 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
+#define IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
+
+#include "base/macros.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
+namespace invalidation {
+class ProfileInvalidationProvider;
+}
+
+namespace ios {
+class ChromeBrowserState;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// A BrowserContextKeyedServiceFactory to construct InvalidationServices wrapped
+// in ProfileInvalidationProviders.
+class IOSChromeProfileInvalidationProviderFactory
+    : public BrowserStateKeyedServiceFactory {
+ public:
+  // Returns the ProfileInvalidationProvider for the given |browser_state|,
+  // lazily creating one first if required.
+  static invalidation::ProfileInvalidationProvider* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+
+  static IOSChromeProfileInvalidationProviderFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      IOSChromeProfileInvalidationProviderFactory>;
+
+  IOSChromeProfileInvalidationProviderFactory();
+  ~IOSChromeProfileInvalidationProviderFactory() override;
+
+  // BrowserStateKeyedServiceFactory:
+  scoped_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+  void RegisterBrowserStatePrefs(
+      user_prefs::PrefRegistrySyncable* registry) override;
+
+  DISALLOW_COPY_AND_ASSIGN(IOSChromeProfileInvalidationProviderFactory);
+};
+
+#endif  // IOS_CHROME_BROWSER_INVALIDATION_IOS_CHROME_PROFILE_INVALIDATION_PROVIDER_FACTORY_H_
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index 74e7156d..4d430bbfe 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -298,6 +298,8 @@
         'browser/install_time_util.mm',
         'browser/installation_notifier.h',
         'browser/installation_notifier.mm',
+        'browser/invalidation/ios_chrome_profile_invalidation_provider_factory.cc',
+        'browser/invalidation/ios_chrome_profile_invalidation_provider_factory.h',
         'browser/ios_chrome_field_trials.cc',
         'browser/ios_chrome_field_trials.h',
         'browser/memory/memory_debugger.h',
diff --git a/mandoline/ui/desktop_ui/browser_window.cc b/mandoline/ui/desktop_ui/browser_window.cc
index 5a9dcaf..2b4ad48 100644
--- a/mandoline/ui/desktop_ui/browser_window.cc
+++ b/mandoline/ui/desktop_ui/browser_window.cc
@@ -27,7 +27,7 @@
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/mus/aura_init.h"
 #include "ui/views/mus/display_converter.h"
-#include "ui/views/mus/native_widget_view_manager.h"
+#include "ui/views/mus/native_widget_mus.h"
 #include "ui/views/widget/widget_delegate.h"
 
 namespace mandoline {
@@ -415,8 +415,8 @@
   views::Widget* widget = new views::Widget;
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.native_widget =
-      new views::NativeWidgetViewManager(widget, app_->shell(), root_);
+  params.native_widget = new views::NativeWidgetMus(
+      widget, app_->shell(), root, mus::mojom::SURFACE_TYPE_DEFAULT);
   params.delegate = widget_delegate;
   params.bounds = root_->bounds();
   widget->Init(params);
diff --git a/mandoline/ui/omnibox/omnibox_application.cc b/mandoline/ui/omnibox/omnibox_application.cc
index 3a17170..631a3863 100644
--- a/mandoline/ui/omnibox/omnibox_application.cc
+++ b/mandoline/ui/omnibox/omnibox_application.cc
@@ -20,7 +20,7 @@
 #include "ui/views/layout/layout_manager.h"
 #include "ui/views/mus/aura_init.h"
 #include "ui/views/mus/display_converter.h"
-#include "ui/views/mus/native_widget_view_manager.h"
+#include "ui/views/mus/native_widget_mus.h"
 #include "ui/views/widget/widget_delegate.h"
 
 namespace mandoline {
@@ -139,8 +139,8 @@
   views::Widget* widget = new views::Widget;
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.native_widget =
-      new views::NativeWidgetViewManager(widget, app_->shell(), root);
+  params.native_widget = new views::NativeWidgetMus(
+      widget, app_->shell(), root, mus::mojom::SURFACE_TYPE_DEFAULT);
   params.delegate = widget_delegate;
   params.bounds = root->bounds();
   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
diff --git a/media/audio/win/audio_device_listener_win.cc b/media/audio/win/audio_device_listener_win.cc
index 056ab7c..505007ba 100644
--- a/media/audio/win/audio_device_listener_win.cc
+++ b/media/audio/win/audio_device_listener_win.cc
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/system_monitor/system_monitor.h"
 #include "base/time/default_tick_clock.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/windows_version.h"
@@ -30,7 +31,7 @@
   }
 }
 
-AudioDeviceListenerWin::AudioDeviceListenerWin(const ListenerCB& listener_cb)
+AudioDeviceListenerWin::AudioDeviceListenerWin(const base::Closure& listener_cb)
     : listener_cb_(listener_cb), tick_clock_(new base::DefaultTickClock()) {
   CHECK(CoreAudioUtil::IsSupported());
 
@@ -97,7 +98,10 @@
 
 STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id,
                                                           DWORD new_state) {
-  listener_cb_.Run(kInputDeviceChange);
+  base::SystemMonitor* monitor = base::SystemMonitor::Get();
+  if (monitor)
+    monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
+
   return S_OK;
 }
 
@@ -127,7 +131,7 @@
       now - last_device_change_time_ >
           base::TimeDelta::FromMilliseconds(kDeviceChangeLimitMs)) {
     last_device_change_time_ = now;
-    listener_cb_.Run(kOutputDeviceChange);
+    listener_cb_.Run();
     did_run_listener_cb = true;
   }
 
diff --git a/media/audio/win/audio_device_listener_win.h b/media/audio/win/audio_device_listener_win.h
index 8875269..053afa6 100644
--- a/media/audio/win/audio_device_listener_win.h
+++ b/media/audio/win/audio_device_listener_win.h
@@ -31,14 +31,10 @@
 // TODO(dalecurtis, henrika): Support input device changes.
 class MEDIA_EXPORT AudioDeviceListenerWin : public IMMNotificationClient {
  public:
-  // Callback returns whether input or output devices have changed.
-  enum DeviceNotificationType { kInputDeviceChange, kOutputDeviceChange };
-  using ListenerCB = base::Callback<void(DeviceNotificationType)>;
-
   // The listener callback will be called from a system level multimedia thread,
   // thus the callee must be thread safe.  |listener| is a permanent callback
   // and must outlive AudioDeviceListenerWin.
-  explicit AudioDeviceListenerWin(const ListenerCB& listener_cb);
+  explicit AudioDeviceListenerWin(const base::Closure& listener_cb);
   virtual ~AudioDeviceListenerWin();
 
  private:
@@ -60,7 +56,7 @@
                                     ERole role,
                                     LPCWSTR new_default_device_id) override;
 
-  ListenerCB listener_cb_;
+  base::Closure listener_cb_;
   ScopedComPtr<IMMDeviceEnumerator> device_enumerator_;
 
   // Used to rate limit device change events.
diff --git a/media/audio/win/audio_device_listener_win_unittest.cc b/media/audio/win/audio_device_listener_win_unittest.cc
index 855c590a..4b78d93 100644
--- a/media/audio/win/audio_device_listener_win_unittest.cc
+++ b/media/audio/win/audio_device_listener_win_unittest.cc
@@ -60,8 +60,8 @@
         base::ASCIIToUTF16(new_device_id).c_str()) == S_OK;
   }
 
-  MOCK_METHOD1(OnDeviceChange,
-               void(AudioDeviceListenerWin::DeviceNotificationType));
+
+  MOCK_METHOD0(OnDeviceChange, void());
 
  private:
   ScopedCOMInitializer com_init_;
@@ -75,16 +75,12 @@
 TEST_F(AudioDeviceListenerWinTest, OutputDeviceChange) {
   ABORT_AUDIO_TEST_IF_NOT(CoreAudioUtil::IsSupported());
 
-  EXPECT_CALL(*this,
-              OnDeviceChange(AudioDeviceListenerWin::kOutputDeviceChange))
-      .Times(1);
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kFirstTestDevice));
 
   testing::Mock::VerifyAndClear(this);
   AdvanceLastDeviceChangeTime();
-  EXPECT_CALL(*this,
-              OnDeviceChange(AudioDeviceListenerWin::kOutputDeviceChange))
-      .Times(1);
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kSecondTestDevice));
 
   // The second device event should be ignored since it occurs too soon.
@@ -96,23 +92,17 @@
 TEST_F(AudioDeviceListenerWinTest, NullOutputDeviceChange) {
   ABORT_AUDIO_TEST_IF_NOT(CoreAudioUtil::IsSupported());
 
-  EXPECT_CALL(*this,
-              OnDeviceChange(AudioDeviceListenerWin::kOutputDeviceChange))
-      .Times(1);
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
   ASSERT_TRUE(SimulateNullDefaultOutputDeviceChange());
 
   testing::Mock::VerifyAndClear(this);
   AdvanceLastDeviceChangeTime();
-  EXPECT_CALL(*this,
-              OnDeviceChange(AudioDeviceListenerWin::kOutputDeviceChange))
-      .Times(1);
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
   ASSERT_TRUE(SimulateDefaultOutputDeviceChange(kFirstTestDevice));
 
   testing::Mock::VerifyAndClear(this);
   AdvanceLastDeviceChangeTime();
-  EXPECT_CALL(*this,
-              OnDeviceChange(AudioDeviceListenerWin::kOutputDeviceChange))
-      .Times(1);
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
   ASSERT_TRUE(SimulateNullDefaultOutputDeviceChange());
 }
 
diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc
index 0b97afbc..70e6c1b 100644
--- a/media/audio/win/audio_manager_win.cc
+++ b/media/audio/win/audio_manager_win.cc
@@ -21,9 +21,9 @@
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/system_monitor/system_monitor.h"
 #include "base/win/windows_version.h"
 #include "media/audio/audio_parameters.h"
+#include "media/audio/win/audio_device_listener_win.h"
 #include "media/audio/win/audio_low_latency_input_win.h"
 #include "media/audio/win/audio_low_latency_output_win.h"
 #include "media/audio/win/audio_manager_win.h"
@@ -171,7 +171,7 @@
     // AudioDeviceListenerWin must be initialized on a COM thread and should
     // only be used if WASAPI / Core Audio is supported.
     output_device_listener_.reset(new AudioDeviceListenerWin(BindToCurrentLoop(
-        base::Bind(&AudioManagerWin::StallAudioThreadAfterDeviceChange,
+        base::Bind(&AudioManagerWin::NotifyAllOutputDeviceChangeListeners,
                    base::Unretained(this)))));
   }
 }
@@ -538,23 +538,6 @@
                                        xp_device_id);
 }
 
-void AudioManagerWin::StallAudioThreadAfterDeviceChange(
-    AudioDeviceListenerWin::DeviceNotificationType notification_type) {
-  base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
-
-  if (notification_type == AudioDeviceListenerWin::kInputDeviceChange) {
-    base::SystemMonitor* monitor = base::SystemMonitor::Get();
-    if (monitor) {
-      monitor->ProcessDevicesChanged(
-          base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
-    }
-    return;
-  }
-
-  DCHECK_EQ(notification_type, AudioDeviceListenerWin::kOutputDeviceChange);
-  NotifyAllOutputDeviceChangeListeners();
-}
-
 /// static
 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
   return new AudioManagerWin(audio_log_factory);
diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h
index 3d33b55..9826566 100644
--- a/media/audio/win/audio_manager_win.h
+++ b/media/audio/win/audio_manager_win.h
@@ -8,10 +8,11 @@
 #include <string>
 
 #include "media/audio/audio_manager_base.h"
-#include "media/audio/win/audio_device_listener_win.h"
 
 namespace media {
 
+class AudioDeviceListenerWin;
+
 // Windows implementation of the AudioManager singleton. This class is internal
 // to the audio output and only internal users can call methods not exposed by
 // the AudioManager class.
@@ -87,12 +88,6 @@
 
   void GetAudioDeviceNamesImpl(bool input, AudioDeviceNames* device_names);
 
-  // We frequently see deadlock in third party Windows audio drivers, so after
-  // a device change is detected, stall for a second before allowing calls into
-  // the Windows audio subsystem.  See http://crbug.com/422522
-  void StallAudioThreadAfterDeviceChange(
-      AudioDeviceListenerWin::DeviceNotificationType notification_type);
-
   // Listen for output device changes.
   scoped_ptr<AudioDeviceListenerWin> output_device_listener_;
 
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index 4fa0d55..3bd2a69 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -5,6 +5,7 @@
 #include "net/socket/tcp_socket.h"
 #include "net/socket/tcp_socket_win.h"
 
+#include <errno.h>
 #include <mstcpip.h>
 
 #include "base/callback_helpers.h"
diff --git a/net/tools/flip_server/acceptor_thread.cc b/net/tools/flip_server/acceptor_thread.cc
index 2b65e5d..4e78663 100644
--- a/net/tools/flip_server/acceptor_thread.cc
+++ b/net/tools/flip_server/acceptor_thread.cc
@@ -7,6 +7,7 @@
 #include <errno.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>  // For TCP_NODELAY
+#include <string.h>       // For strerror
 #include <sys/socket.h>
 #include <sys/types.h>
 
diff --git a/net/tools/flip_server/create_listener.cc b/net/tools/flip_server/create_listener.cc
index 1fc5a7e..3e97603 100644
--- a/net/tools/flip_server/create_listener.cc
+++ b/net/tools/flip_server/create_listener.cc
@@ -11,6 +11,7 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/net/tools/flip_server/flip_in_mem_edsm_server.cc b/net/tools/flip_server/flip_in_mem_edsm_server.cc
index 1e6a8cef..ed449f8 100644
--- a/net/tools/flip_server/flip_in_mem_edsm_server.cc
+++ b/net/tools/flip_server/flip_in_mem_edsm_server.cc
@@ -5,6 +5,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 
diff --git a/net/tools/quic/quic_socket_utils.cc b/net/tools/quic/quic_socket_utils.cc
index 3bc0db0..c2d65409 100644
--- a/net/tools/quic/quic_socket_utils.cc
+++ b/net/tools/quic/quic_socket_utils.cc
@@ -6,6 +6,7 @@
 
 #include <errno.h>
 #include <netinet/in.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 #include <string>
diff --git a/remoting/webapp/crd/js/it2me_activity.js b/remoting/webapp/crd/js/it2me_activity.js
index c05d2192..1e69c63 100644
--- a/remoting/webapp/crd/js/it2me_activity.js
+++ b/remoting/webapp/crd/js/it2me_activity.js
@@ -46,28 +46,41 @@
 
 remoting.It2MeActivity.prototype.start = function() {
   var that = this;
+  var SessionState = remoting.ChromotingEvent.SessionState;
 
   this.logger_ = this.createLogger_();
-  this.logger_.logSessionStateChange(
-      remoting.ChromotingEvent.SessionState.STARTED);
+  this.logger_.logSessionStateChange(SessionState.STARTED);
 
+  console.assert(
+      !this.desktopActivity_, 'Zombie DesktopActivity from previous session');
+  base.dispose(this.desktopActivity_);
   this.desktopActivity_ =
       new remoting.DesktopRemotingActivity(this, this.logger_);
 
+  function onError(/** remoting.Error */ error) {
+    if (error.isCancel()) {
+      that.logger_.logSessionStateChange(SessionState.CONNECTION_CANCELED);
+      remoting.setMode(remoting.AppMode.HOME);
+    } else {
+      that.logger_.logSessionStateChange(SessionState.CONNECTION_FAILED, error);
+      that.showErrorMessage_(error);
+    }
+
+    base.dispose(that.desktopActivity_);
+    that.desktopActivity_ = null;
+  }
+
+  var sessionStart = Date.now();
+
   this.accessCodeDialog_.show().then(function(/** string */ accessCode) {
+    that.logger_.setAuthTotalTime(Date.now() - sessionStart);
     that.desktopActivity_.getConnectingDialog().show();
     return that.verifyAccessCode_(accessCode);
   }).then(function() {
     return remoting.HostListApi.getInstance().getSupportHost(that.hostId_);
   }).then(function(/** remoting.Host */ host) {
     that.connect_(host);
-  }).catch(remoting.Error.handler(function(/** remoting.Error */ error) {
-    if (error.hasTag(remoting.Error.Tag.CANCELLED)) {
-      remoting.setMode(remoting.AppMode.HOME);
-    } else {
-      that.showErrorMessage_(error);
-    }
-  }));
+  }).catch(remoting.Error.handler(onError));
 };
 
 
diff --git a/testing/android/junit/java/src/org/chromium/testing/local/CustomShadowAsyncTask.java b/testing/android/junit/java/src/org/chromium/testing/local/CustomShadowAsyncTask.java
new file mode 100644
index 0000000..6c81130
--- /dev/null
+++ b/testing/android/junit/java/src/org/chromium/testing/local/CustomShadowAsyncTask.java
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.testing.local;
+
+import android.os.AsyncTask;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowAsyncTask;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Forces async tasks to execute with the default executor.
+ * This works around Robolectric not working out of the box with custom executors.
+ */
+@Implements(AsyncTask.class)
+public class CustomShadowAsyncTask<Params, Progress, Result>
+        extends ShadowAsyncTask<Params, Progress, Result> {
+    @Implementation
+    public AsyncTask<Params, Progress, Result> executeOnExecutor(
+            Executor executor, Params... params) {
+        return super.execute(params);
+    }
+}
diff --git a/testing/buildbot/chromium.json b/testing/buildbot/chromium.json
index e9127b2..fb9c356 100644
--- a/testing/buildbot/chromium.json
+++ b/testing/buildbot/chromium.json
@@ -1,8 +1,5 @@
 {
   "Linux x64": {
-    "additional_compile_targets": [
-      "all"
-    ],
     "scripts": [
       {
         "name": "sizes",
diff --git a/testing/buildbot/trybot_analyze_config.json b/testing/buildbot/trybot_analyze_config.json
index da3533e..16112d1 100644
--- a/testing/buildbot/trybot_analyze_config.json
+++ b/testing/buildbot/trybot_analyze_config.json
@@ -9,6 +9,8 @@
       ".*isolate",
       "build/.*gyp[i]?",
       "build/android/.*py",
+      "build/android/devil/.*",
+      "build/android/play_services/.*",
       "build/android/pylib/.*",
       "build/compiler_version.py",
       "build/get_landmines.py",
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getSources-expected.txt b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getSources-expected.txt
index 2e6d9c2..76f45bb 100644
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getSources-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getSources-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: MediaStreamTrack.getSources is deprecated. See https://www.chromestatus.com/feature/4765305641369600 for more details.
 Tests MediaStreamTrack::getSources.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt
index 179bf6a..4a51f22 100644
--- a/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/animation/animation-timeline-expected.txt
@@ -3,21 +3,21 @@
 >>>> Animation with start delay only
 WebAnimation
 <div class="animation-name" style="transform: translateX(229.22px); width: 444px;"></div>
-<svg class="animation-ui" height="50" width="680.67" style="margin-left: -7px; transform: translateX(0px);"><g style="transform: translateX(222.22px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="451.44" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.02 10.85 L 0.94 10.85 L 3.51 10.85 L 7.66 10.85 L 13.28 10.85 L 20.28 10.85 L 28.59 10.85 L 38.10 10.85 L 48.73 10.85 L 60.39 10.85 L 72.99 10.85 L 86.44 10.85 L 100.65 10.85 L 115.54 10.85 L 131.00 10.85 L 146.95 10.85 L 163.31 10.85 L 179.98 10.85 L 196.87 10.85 L 213.90 10.85 L 230.96 10.85 L 247.99 10.85 L 264.88 10.85 L 281.54 10.85 L 297.89 10.85 L 313.83 10.85 L 329.28 10.85 L 344.15 10.85 L 358.34 10.85 L 371.77 10.85 L 384.35 10.85 L 395.99 10.85 L 406.60 10.85 L 416.08 10.85 L 424.36 10.85 L 431.33 10.85 L 436.92 10.85 L 441.03 10.85 L 443.56 10.85 L 444.44 10.85 L 444.44 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="451.44" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="229.22" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
+<svg class="animation-ui" height="50" style="margin-left: -7px; transform: translateX(0px);" width="680.67"><g style="transform: translateX(222.22px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="451.44" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.02 10.85 L 0.94 10.85 L 3.51 10.85 L 7.66 10.85 L 13.28 10.85 L 20.28 10.85 L 28.59 10.85 L 38.10 10.85 L 48.73 10.85 L 60.39 10.85 L 72.99 10.85 L 86.44 10.85 L 100.65 10.85 L 115.54 10.85 L 131.00 10.85 L 146.95 10.85 L 163.31 10.85 L 179.98 10.85 L 196.87 10.85 L 213.90 10.85 L 230.96 10.85 L 247.99 10.85 L 264.88 10.85 L 281.54 10.85 L 297.89 10.85 L 313.83 10.85 L 329.28 10.85 L 344.15 10.85 L 358.34 10.85 L 371.77 10.85 L 384.35 10.85 L 395.99 10.85 L 406.60 10.85 L 416.08 10.85 L 424.36 10.85 L 431.33 10.85 L 436.92 10.85 L 441.03 10.85 L 443.56 10.85 L 444.44 10.85 L 444.44 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="451.44" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="229.22" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
 >>>> Animation with start and end delay
 WebAnimation
 <div class="animation-name" style="transform: translateX(10.28px); width: 600px;"></div>
-<svg class="animation-ui" height="50" width="680.67" style="margin-left: -7px; transform: translateX(0px);"><g style="transform: translateX(3.28px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="663.81" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.03 10.85 L 1.39 10.85 L 5.19 10.85 L 11.32 10.85 L 19.62 10.85 L 29.97 10.85 L 42.25 10.85 L 56.31 10.85 L 72.02 10.85 L 89.25 10.85 L 107.87 10.85 L 127.75 10.85 L 148.75 10.85 L 170.74 10.85 L 193.59 10.85 L 217.17 10.85 L 241.34 10.85 L 265.98 10.85 L 290.94 10.85 L 316.10 10.85 L 341.33 10.85 L 366.49 10.85 L 391.44 10.85 L 416.07 10.85 L 440.23 10.85 L 463.79 10.85 L 486.62 10.85 L 508.59 10.85 L 529.57 10.85 L 549.42 10.85 L 568.01 10.85 L 585.21 10.85 L 600.88 10.85 L 614.90 10.85 L 627.13 10.85 L 637.44 10.85 L 645.70 10.85 L 651.76 10.85 L 655.51 10.85 L 656.81 10.85 L 656.81 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="663.81" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="10.28" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="13.57" style="stroke: black; transform: translateX(660.1px);"></line></svg>
+<svg class="animation-ui" height="50" style="margin-left: -7px; transform: translateX(0px);" width="680.67"><g style="transform: translateX(3.28px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="663.81" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.03 10.85 L 1.39 10.85 L 5.19 10.85 L 11.32 10.85 L 19.62 10.85 L 29.97 10.85 L 42.25 10.85 L 56.31 10.85 L 72.02 10.85 L 89.25 10.85 L 107.87 10.85 L 127.75 10.85 L 148.75 10.85 L 170.74 10.85 L 193.59 10.85 L 217.17 10.85 L 241.34 10.85 L 265.98 10.85 L 290.94 10.85 L 316.10 10.85 L 341.33 10.85 L 366.49 10.85 L 391.44 10.85 L 416.07 10.85 L 440.23 10.85 L 463.79 10.85 L 486.62 10.85 L 508.59 10.85 L 529.57 10.85 L 549.42 10.85 L 568.01 10.85 L 585.21 10.85 L 600.88 10.85 L 614.90 10.85 L 627.13 10.85 L 637.44 10.85 L 645.70 10.85 L 651.76 10.85 L 655.51 10.85 L 656.81 10.85 L 656.81 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="663.81" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="10.28" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="13.57" style="stroke: black; transform: translateX(660.1px);"></line></svg>
 >>>> Animation with step timing function
 WebAnimation
 <div class="animation-name" style="transform: translateX(7px); width: 666px;"></div>
-<svg class="animation-ui" height="50" width="680.67" style="margin-left: -7px; transform: translateX(0px);"><g style="transform: translateX(0px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.03 10.85 L 1.41 10.85 L 5.27 10.85 L 11.49 10.85 L 19.91 10.85 L 30.42 10.85 L 42.88 10.85 L 57.15 10.85 L 73.10 10.85 L 90.59 10.85 L 109.49 10.85 L 129.66 10.85 L 150.98 10.85 L 173.30 10.85 L 196.50 10.85 L 220.43 10.85 L 244.96 10.85 L 269.97 10.85 L 295.31 10.85 L 320.84 10.85 L 346.45 10.85 L 371.98 10.85 L 397.31 10.85 L 422.31 10.85 L 446.83 10.85 L 470.75 10.85 L 493.92 10.85 L 516.22 10.85 L 537.51 10.85 L 557.66 10.85 L 576.53 10.85 L 593.99 10.85 L 609.90 10.85 L 624.13 10.85 L 636.54 10.85 L 647.00 10.85 L 655.38 10.85 L 661.54 10.85 L 665.35 10.85 L 666.67 10.85 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
+<svg class="animation-ui" height="50" style="margin-left: -7px; transform: translateX(0px);" width="680.67"><g style="transform: translateX(0px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.03 10.85 L 1.41 10.85 L 5.27 10.85 L 11.49 10.85 L 19.91 10.85 L 30.42 10.85 L 42.88 10.85 L 57.15 10.85 L 73.10 10.85 L 90.59 10.85 L 109.49 10.85 L 129.66 10.85 L 150.98 10.85 L 173.30 10.85 L 196.50 10.85 L 220.43 10.85 L 244.96 10.85 L 269.97 10.85 L 295.31 10.85 L 320.84 10.85 L 346.45 10.85 L 371.98 10.85 L 397.31 10.85 L 422.31 10.85 L 446.83 10.85 L 470.75 10.85 L 493.92 10.85 L 516.22 10.85 L 537.51 10.85 L 557.66 10.85 L 576.53 10.85 L 593.99 10.85 L 609.90 10.85 L 624.13 10.85 L 636.54 10.85 L 647.00 10.85 L 655.38 10.85 L 661.54 10.85 L 665.35 10.85 L 666.67 10.85 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
 >>>> CSS animation started
 CSSAnimation
 <div class="animation-name" style="transform: translateX(7px); width: 666px;">anim</div>
-<svg class="animation-ui" height="50" width="680.67" style="margin-left: -7px; transform: translateX(0px);"><g style="transform: translateX(0px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.52 24.97 L 21.68 22.90 L 42.20 20.86 L 62.11 18.88 L 81.45 16.99 L 100.24 15.22 L 118.53 13.59 L 136.34 12.11 L 153.72 10.79 L 170.70 9.63 L 187.31 8.62 L 203.59 7.75 L 219.57 7.02 L 235.29 6.41 L 250.79 5.91 L 266.09 5.51 L 281.23 5.20 L 296.26 4.97 L 311.20 4.83 L 326.08 4.76 L 340.95 4.76 L 355.83 4.83 L 370.77 4.97 L 385.80 5.20 L 400.95 5.51 L 416.26 5.91 L 431.76 6.41 L 447.48 7.02 L 463.47 7.75 L 479.76 8.62 L 496.38 9.63 L 513.36 10.79 L 530.75 12.11 L 548.58 13.59 L 566.87 15.22 L 585.68 16.99 L 605.03 18.88 L 624.95 20.86 L 645.48 22.90 L 666.67 24.97 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
+<svg class="animation-ui" height="50" style="margin-left: -7px; transform: translateX(0px);" width="680.67"><g style="transform: translateX(0px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.52 24.97 L 21.68 22.90 L 42.20 20.86 L 62.11 18.88 L 81.45 16.99 L 100.24 15.22 L 118.53 13.59 L 136.34 12.11 L 153.72 10.79 L 170.70 9.63 L 187.31 8.62 L 203.59 7.75 L 219.57 7.02 L 235.29 6.41 L 250.79 5.91 L 266.09 5.51 L 281.23 5.20 L 296.26 4.97 L 311.20 4.83 L 326.08 4.76 L 340.95 4.76 L 355.83 4.83 L 370.77 4.97 L 385.80 5.20 L 400.95 5.51 L 416.26 5.91 L 431.76 6.41 L 447.48 7.02 L 463.47 7.75 L 479.76 8.62 L 496.38 9.63 L 513.36 10.79 L 530.75 12.11 L 548.58 13.59 L 566.87 15.22 L 585.68 16.99 L 605.03 18.88 L 624.95 20.86 L 645.48 22.90 L 666.67 24.97 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><g class="animation-tail-iterations"></g></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
 >>>> CSS transition started
 CSSTransition
 <div class="animation-name" style="transform: translateX(7px); width: 666px;">background-color</div>
-<svg class="animation-ui" height="50" width="680.67" style="margin-left: -7px; transform: translateX(0px);"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.02 0.00 L 0.71 0.00 L 2.69 0.00 L 5.90 0.02 L 10.33 0.11 L 15.93 0.31 L 22.68 0.66 L 30.54 1.15 L 39.47 1.76 L 49.44 2.45 L 60.43 3.21 L 72.38 4.00 L 85.28 4.82 L 99.09 5.64 L 113.77 6.47 L 129.29 7.29 L 145.62 8.10 L 162.73 8.90 L 180.57 9.69 L 199.12 10.46 L 218.34 11.23 L 238.20 11.98 L 258.66 12.72 L 279.70 13.46 L 301.28 14.19 L 323.36 14.91 L 345.91 15.63 L 368.90 16.35 L 392.29 17.08 L 416.06 17.80 L 440.16 18.53 L 464.56 19.26 L 489.23 20.01 L 514.14 20.76 L 539.25 21.52 L 564.53 22.30 L 589.95 23.09 L 615.46 23.90 L 641.05 24.72 L 666.67 25.57 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
+<svg class="animation-ui" height="50" style="margin-left: -7px; transform: translateX(0px);" width="680.67"><g style="transform: translateX(0px);"><line class="animation-line" x1="7" y1="26" y2="26" x2="673.67" style="stroke: black;"></line><path class="animation-keyframe" d="M 0 26 L 0.02 0.00 L 0.71 0.00 L 2.69 0.00 L 5.90 0.02 L 10.33 0.11 L 15.93 0.31 L 22.68 0.66 L 30.54 1.15 L 39.47 1.76 L 49.44 2.45 L 60.43 3.21 L 72.38 4.00 L 85.28 4.82 L 99.09 5.64 L 113.77 6.47 L 129.29 7.29 L 145.62 8.10 L 162.73 8.90 L 180.57 9.69 L 199.12 10.46 L 218.34 11.23 L 238.20 11.98 L 258.66 12.72 L 279.70 13.46 L 301.28 14.19 L 323.36 14.91 L 345.91 15.63 L 368.90 16.35 L 392.29 17.08 L 416.06 17.80 L 440.16 18.53 L 464.56 19.26 L 489.23 20.01 L 514.14 20.76 L 539.25 21.52 L 564.53 22.30 L 589.95 23.09 L 615.46 23.90 L 641.05 24.72 L 666.67 25.57 L 666.67 26 Z" style="transform: translateX(7px); fill: black;"></path><circle class="animation-endpoint" cx="7.00" cy="26" r="3.5" style="stroke: black; fill: black;"></circle><circle class="animation-endpoint" cx="673.67" cy="26" r="3.5" style="stroke: black; fill: black;"></circle></g><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black;"></line><line class="animation-delay-line" x1="7" y1="26" y2="26" x2="7.00" style="stroke: black; transform: translateX(666.67px);"></line></svg>
 
diff --git a/third_party/WebKit/ManualTests/animation/animateMotion-to.svg b/third_party/WebKit/ManualTests/animation/animateMotion-to.svg
deleted file mode 100644
index 17e2cdce..0000000
--- a/third_party/WebKit/ManualTests/animation/animateMotion-to.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<svg xmlns='http://www.w3.org/2000/svg'>
-  <rect width='100' height='100'>
-    <animateMotion to='100,0' dur='3s' />
-  </rect>
-  <text x='10' y='120'>
-    The rect should from 0,0 to 100,0 over 3 seconds.
-  </text>  
-</svg>
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index e5f1af4..fb40bcc7 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -404,8 +404,8 @@
     , m_parsingState(FinishedParsing)
     , m_gotoAnchorNeededAfterStylesheetsLoad(false)
     , m_containsValidityStyleRules(false)
-    , m_updateFocusAppearanceRestoresSelection(false)
     , m_containsPlugins(false)
+    , m_updateFocusAppearanceSelectionBahavior(SelectionBehaviorOnFocus::Reset)
     , m_ignoreDestructiveWriteCount(0)
     , m_markers(adoptPtrWillBeNoop(new DocumentMarkerController))
     , m_updateFocusAppearanceTimer(this, &Document::updateFocusAppearanceTimerFired)
@@ -4902,9 +4902,9 @@
     return isMainThread();
 }
 
-void Document::updateFocusAppearanceSoon(bool restorePreviousSelection)
+void Document::updateFocusAppearanceSoon(SelectionBehaviorOnFocus selectionbehavioronfocus)
 {
-    m_updateFocusAppearanceRestoresSelection = restorePreviousSelection;
+    m_updateFocusAppearanceSelectionBahavior = selectionbehavioronfocus;
     if (!m_updateFocusAppearanceTimer.isActive())
         m_updateFocusAppearanceTimer.startOneShot(0, BLINK_FROM_HERE);
 }
@@ -4921,7 +4921,7 @@
         return;
     updateLayout();
     if (element->isFocusable())
-        element->updateFocusAppearance(m_updateFocusAppearanceRestoresSelection);
+        element->updateFocusAppearance(m_updateFocusAppearanceSelectionBahavior);
 }
 
 void Document::attachRange(Range* range)
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index a37c362e..b1a529e 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -168,7 +168,7 @@
 class TreeWalker;
 class VisitedLinkState;
 class WebGLRenderingContext;
-
+enum class SelectionBehaviorOnFocus;
 struct AnnotatedRegionValue;
 struct IconURL;
 
@@ -803,7 +803,7 @@
     void setUseSecureKeyboardEntryWhenActive(bool);
     bool useSecureKeyboardEntryWhenActive() const;
 
-    void updateFocusAppearanceSoon(bool restorePreviousSelection);
+    void updateFocusAppearanceSoon(SelectionBehaviorOnFocus);
     void cancelFocusAppearanceUpdate();
 
     bool isDNSPrefetchEnabled() const { return m_isDNSPrefetchEnabled; }
@@ -1246,8 +1246,8 @@
     bool m_isDNSPrefetchEnabled;
     bool m_haveExplicitlyDisabledDNSPrefetch;
     bool m_containsValidityStyleRules;
-    bool m_updateFocusAppearanceRestoresSelection;
     bool m_containsPlugins;
+    SelectionBehaviorOnFocus m_updateFocusAppearanceSelectionBahavior;
 
     // http://www.whatwg.org/specs/web-apps/current-work/#ignore-destructive-writes-counter
     unsigned m_ignoreDestructiveWriteCount;
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 6caa421..ff7212a 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -2348,7 +2348,7 @@
     return elementData()->attributes().find(qName);
 }
 
-void Element::focus(bool restorePreviousSelection, WebFocusType type, InputDeviceCapabilities* sourceCapabilities)
+void Element::focus(const FocusParams& params)
 {
     if (!inDocument())
         return;
@@ -2370,13 +2370,13 @@
         // Slide the focus to its inner node.
         Element* next = document().page()->focusController().findFocusableElement(WebFocusTypeForward, *this);
         if (next && containsIncludingShadowDOM(next)) {
-            next->focus(false, WebFocusTypeForward);
+            next->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr));
             return;
         }
     }
 
     RefPtrWillBeRawPtr<Node> protect(this);
-    if (!document().page()->focusController().setFocusedElement(this, document().frame(), type, sourceCapabilities))
+    if (!document().page()->focusController().setFocusedElement(this, document().frame(), params.type, params.sourceCapabilities))
         return;
 
     // Setting the focused node above might have invalidated the layout due to scripts.
@@ -2385,7 +2385,7 @@
         return;
 
     cancelFocusAppearanceUpdate();
-    updateFocusAppearance(restorePreviousSelection);
+    updateFocusAppearance(params.selectionBehavior);
 
     if (UserGestureIndicator::processedUserGestureSinceLoad()) {
         // Bring up the keyboard in the context of anything triggered by a user
@@ -2396,7 +2396,7 @@
     }
 }
 
-void Element::updateFocusAppearance(bool /*restorePreviousSelection*/)
+void Element::updateFocusAppearance(SelectionBehaviorOnFocus)
 {
     if (isRootEditableElement()) {
         // Taking the ownership since setSelection() may release the last reference to |frame|.
diff --git a/third_party/WebKit/Source/core/dom/Element.h b/third_party/WebKit/Source/core/dom/Element.h
index c39e834..12520790 100644
--- a/third_party/WebKit/Source/core/dom/Element.h
+++ b/third_party/WebKit/Source/core/dom/Element.h
@@ -87,6 +87,25 @@
 
 enum class ShadowRootType;
 
+enum class SelectionBehaviorOnFocus {
+    Reset,
+    Restore,
+};
+
+struct FocusParams {
+    STACK_ALLOCATED();
+
+    FocusParams() {}
+    FocusParams(SelectionBehaviorOnFocus selection, WebFocusType focusType, InputDeviceCapabilities* capabilities)
+        : selectionBehavior(selection)
+        , type(focusType)
+        , sourceCapabilities(capabilities) {}
+
+    SelectionBehaviorOnFocus selectionBehavior = SelectionBehaviorOnFocus::Restore;
+    WebFocusType type = WebFocusTypeNone;
+    Member<InputDeviceCapabilities> sourceCapabilities = nullptr;
+};
+
 typedef WillBeHeapVector<RefPtrWillBeMember<Attr>> AttrNodeList;
 
 class CORE_EXPORT Element : public ContainerNode {
@@ -371,8 +390,8 @@
     virtual const AtomicString imageSourceURL() const;
     virtual Image* imageContents() { return nullptr; }
 
-    virtual void focus(bool restorePreviousSelection = true, WebFocusType = WebFocusTypeNone, InputDeviceCapabilities* sourceCapabilities = nullptr);
-    virtual void updateFocusAppearance(bool restorePreviousSelection);
+    virtual void focus(const FocusParams& = FocusParams());
+    virtual void updateFocusAppearance(SelectionBehaviorOnFocus);
     virtual void blur();
 
     void setDistributeScroll(ScrollStateCallback*, String nativeScrollBehavior);
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
index 2e11e86..6bd3f6a 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
@@ -563,11 +563,11 @@
             //   FirstLetter seem to have different ideas of where things can split.
             //   FirstLetter takes the punctuation + first letter, and BIDI will
             //   split out the punctuation and possibly reorder it.
-            if (nextTextBox && nextTextBox->layoutObject() != layoutObject) {
+            if (nextTextBox && !(nextTextBox->lineLayoutItem().isEqual(layoutObject))) {
                 m_textBox = 0;
                 return;
             }
-            ASSERT(!nextTextBox || nextTextBox->layoutObject() == layoutObject);
+            ASSERT(!nextTextBox || nextTextBox->lineLayoutItem().isEqual(layoutObject));
 
             if (runStart < runEnd) {
                 // Handle either a single newline character (which becomes a space),
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.cpp b/third_party/WebKit/Source/core/frame/UseCounter.cpp
index 11b0dc8d..c7c7867 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.cpp
+++ b/third_party/WebKit/Source/core/frame/UseCounter.cpp
@@ -955,6 +955,9 @@
     case V8SVGElement_OffsetHeight_AttributeGetter:
         return "'SVGElement.offsetHeight' is deprecated and will be removed in M50, around April 2016. See https://www.chromestatus.com/features/5724912467574784 for more details.";
 
+    case MediaStreamTrackGetSources:
+        return "MediaStreamTrack.getSources is deprecated. See https://www.chromestatus.com/feature/4765305641369600 for more details.";
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
         return String();
diff --git a/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp b/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
index 82d63475d..19d45c8 100644
--- a/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
@@ -222,16 +222,13 @@
     toLayoutImage(layoutObject)->areaElementFocusChanged(this);
 }
 
-void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection)
+void HTMLAreaElement::updateFocusAppearance(SelectionBehaviorOnFocus selectionBehavior)
 {
     if (!isFocusable())
         return;
 
-    HTMLImageElement* imageElement = this->imageElement();
-    if (!imageElement)
-        return;
-
-    imageElement->updateFocusAppearance(restorePreviousSelection);
+    if (HTMLImageElement* imageElement = this->imageElement())
+        imageElement->updateFocusAppearance(selectionBehavior);
 }
 
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLAreaElement.h b/third_party/WebKit/Source/core/html/HTMLAreaElement.h
index 1513f8f..0659add 100644
--- a/third_party/WebKit/Source/core/html/HTMLAreaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLAreaElement.h
@@ -56,7 +56,7 @@
     bool isKeyboardFocusable() const override;
     bool isMouseFocusable() const override;
     bool layoutObjectIsFocusable() const override;
-    void updateFocusAppearance(bool /*restorePreviousSelection*/) override;
+    void updateFocusAppearance(SelectionBehaviorOnFocus) override;
     void setFocus(bool) override;
 
     enum Shape { Default, Poly, Rect, Circle, Unknown };
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
index ba6302a..a16d3980 100644
--- a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
@@ -340,17 +340,21 @@
     return m_inputType->shouldShowFocusRingOnMouseFocus();
 }
 
-void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
+void HTMLInputElement::updateFocusAppearance(SelectionBehaviorOnFocus selectionBehavior)
 {
     if (isTextField()) {
-        if (!restorePreviousSelection)
+        switch (selectionBehavior) {
+        case SelectionBehaviorOnFocus::Reset:
             select(NotDispatchSelectEvent);
-        else
+            break;
+        case SelectionBehaviorOnFocus::Restore:
             restoreCachedSelection();
+            break;
+        }
         if (document().frame())
             document().frame()->selection().revealSelection();
     } else {
-        HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection);
+        HTMLTextFormControlElement::updateFocusAppearance(selectionBehavior);
     }
 }
 
@@ -504,7 +508,7 @@
     }
 
     if (document().focusedElement() == this)
-        document().updateFocusAppearanceSoon(true /* restore selection */);
+        document().updateFocusAppearanceSoon(SelectionBehaviorOnFocus::Restore);
 
     setTextAsOfLastFormControlChangeEvent(value());
     setChangedSinceLastFormControlChangeEvent(false);
@@ -831,7 +835,7 @@
     m_inputType->countUsage();
 
     if (document().focusedElement() == this)
-        document().updateFocusAppearanceSoon(true /* restore selection */);
+        document().updateFocusAppearanceSoon(SelectionBehaviorOnFocus::Restore);
 }
 
 void HTMLInputElement::detach(const AttachContext& context)
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.h b/third_party/WebKit/Source/core/html/HTMLInputElement.h
index b79b6eb..fec9689 100644
--- a/third_party/WebKit/Source/core/html/HTMLInputElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLInputElement.h
@@ -150,7 +150,7 @@
     bool layoutObjectIsNeeded(const ComputedStyle&) final;
     LayoutObject* createLayoutObject(const ComputedStyle&) override;
     void detach(const AttachContext& = AttachContext()) final;
-    void updateFocusAppearance(bool restorePreviousSelection) final;
+    void updateFocusAppearance(SelectionBehaviorOnFocus) final;
 
     // FIXME: For isActivatedSubmit and setActivatedSubmit, we should use the NVI-idiom here by making
     // it private virtual in all classes and expose a public method in HTMLFormControlElement to call
diff --git a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
index 0268776b..c32e3b8 100644
--- a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
@@ -186,7 +186,7 @@
             // In case of double click or triple click, selection will be there,
             // so do not focus the control element.
             if (!isLabelTextSelected)
-                element->focus(true, WebFocusTypeMouse);
+                element->focus(FocusParams(SelectionBehaviorOnFocus::Restore, WebFocusTypeMouse, nullptr));
         }
 
         // Click the corresponding control.
@@ -208,13 +208,13 @@
     return HTMLElement::willRespondToMouseClickEvents();
 }
 
-void HTMLLabelElement::focus(bool, WebFocusType type, InputDeviceCapabilities* sourceCapabilities)
+void HTMLLabelElement::focus(const FocusParams& params)
 {
     // to match other browsers, always restore previous selection
     if (HTMLElement* element = control())
-        element->focus(true, type, sourceCapabilities);
+        element->focus(FocusParams(SelectionBehaviorOnFocus::Restore, params.type, params.sourceCapabilities));
     if (isFocusable())
-        HTMLElement::focus(true, type, sourceCapabilities);
+        HTMLElement::focus(params);
 }
 
 void HTMLLabelElement::accessKeyAction(bool sendMouseEvents)
diff --git a/third_party/WebKit/Source/core/html/HTMLLabelElement.h b/third_party/WebKit/Source/core/html/HTMLLabelElement.h
index fca7c915..9c9fd06 100644
--- a/third_party/WebKit/Source/core/html/HTMLLabelElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLLabelElement.h
@@ -69,7 +69,7 @@
     // Overridden to either click() or focus() the corresponding control.
     void defaultEventHandler(Event*) override;
 
-    void focus(bool restorePreviousSelection, WebFocusType, InputDeviceCapabilities* sourceCapabilities) override;
+    void focus(const FocusParams&) override;
 
     // FormAssociatedElement methods
     bool isFormControlElement() const override { return false; }
diff --git a/third_party/WebKit/Source/core/html/HTMLLegendElement.cpp b/third_party/WebKit/Source/core/html/HTMLLegendElement.cpp
index b548d3bf..19e95de 100644
--- a/third_party/WebKit/Source/core/html/HTMLLegendElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLegendElement.cpp
@@ -54,16 +54,16 @@
     return Traversal<HTMLFormControlElement>::next(*fieldset, fieldset);
 }
 
-void HTMLLegendElement::focus(bool, WebFocusType type, InputDeviceCapabilities* sourceCapabilities)
+void HTMLLegendElement::focus(const FocusParams& params)
 {
     if (isFocusable()) {
-        Element::focus(true, type, sourceCapabilities);
+        Element::focus(params);
         return;
     }
 
     // To match other browsers' behavior, never restore previous selection.
     if (HTMLFormControlElement* control = associatedControl())
-        control->focus(false, type, sourceCapabilities);
+        control->focus(FocusParams(SelectionBehaviorOnFocus::Reset, params.type, params.sourceCapabilities));
 }
 
 void HTMLLegendElement::accessKeyAction(bool sendMouseEvents)
diff --git a/third_party/WebKit/Source/core/html/HTMLLegendElement.h b/third_party/WebKit/Source/core/html/HTMLLegendElement.h
index dedea4d3..dc8763815 100644
--- a/third_party/WebKit/Source/core/html/HTMLLegendElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLLegendElement.h
@@ -44,7 +44,7 @@
     HTMLFormControlElement* associatedControl();
 
     void accessKeyAction(bool sendMouseEvents) override;
-    void focus(bool restorePreviousSelection, WebFocusType, InputDeviceCapabilities* sourceCapabilities) override;
+    void focus(const FocusParams&) override;
 };
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
index 55cec81..358e5f49 100644
--- a/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLSelectElement.cpp
@@ -1335,7 +1335,7 @@
 
     if (event->type() == EventTypeNames::mousedown && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
         InputDeviceCapabilities* sourceCapabilities = toMouseEvent(event)->fromTouch() ? InputDeviceCapabilities::firesTouchEventsSourceCapabilities() : InputDeviceCapabilities::doesntFireTouchEventsSourceCapabilities();
-        focus(true, WebFocusTypeNone, sourceCapabilities);
+        focus(FocusParams(SelectionBehaviorOnFocus::Restore, WebFocusTypeNone, sourceCapabilities));
         if (layoutObject() && layoutObject()->isMenuList() && !isDisabledFormControl()) {
             if (popupIsVisible()) {
                 hidePopup();
diff --git a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
index 635a6a48..6c1aa8c 100644
--- a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp
@@ -241,13 +241,16 @@
     return true;
 }
 
-void HTMLTextAreaElement::updateFocusAppearance(bool restorePreviousSelection)
+void HTMLTextAreaElement::updateFocusAppearance(SelectionBehaviorOnFocus selectionBehavior)
 {
-    if (!restorePreviousSelection)
+    switch (selectionBehavior) {
+    case SelectionBehaviorOnFocus::Reset:
         setSelectionRange(0, 0, SelectionHasNoDirection, NotDispatchSelectEvent);
-    else
+        break;
+    case SelectionBehaviorOnFocus::Restore:
         restoreCachedSelection();
-
+        break;
+    }
     if (document().frame())
         document().frame()->selection().revealSelection();
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
index 80f8dcb..62c2186 100644
--- a/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h
@@ -125,7 +125,7 @@
     bool hasCustomFocusLogic() const override;
     bool shouldShowFocusRingOnMouseFocus() const override;
     bool isKeyboardFocusable() const override;
-    void updateFocusAppearance(bool restorePreviousSelection) override;
+    void updateFocusAppearance(SelectionBehaviorOnFocus) override;
 
     void accessKeyAction(bool sendMouseEvents) override;
 
diff --git a/third_party/WebKit/Source/core/html/forms/InputType.cpp b/third_party/WebKit/Source/core/html/forms/InputType.cpp
index c986cda..5feee4b7 100644
--- a/third_party/WebKit/Source/core/html/forms/InputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/InputType.cpp
@@ -489,7 +489,7 @@
 
 void InputType::accessKeyAction(bool)
 {
-    element().focus(false);
+    element().focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeNone, nullptr));
 }
 
 void InputType::countUsage()
diff --git a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
index 1cf07fb..849905f 100644
--- a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
@@ -352,7 +352,7 @@
             rpContainer->appendChild(editingViewPort.release());
             rpContainer->appendChild(DataListIndicatorElement::create(document));
             if (element().document().focusedElement() == element())
-                element().updateFocusAppearance(true /* restore selection */);
+                element().updateFocusAppearance(SelectionBehaviorOnFocus::Restore);
         }
     } else {
         picker->remove(ASSERT_NO_EXCEPTION);
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index 0f681c7..1f84d71d 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -1837,7 +1837,7 @@
         Element* next = page->focusController().findFocusableElement(WebFocusTypeForward, *element.authorShadowRoot());
         if (next && element.containsIncludingShadowDOM(next)) {
             // Use WebFocusTypeForward instead of WebFocusTypeMouse here to mean the focus has slided.
-            next->focus(false, WebFocusTypeForward);
+            next->focus(FocusParams(SelectionBehaviorOnFocus::Reset, WebFocusTypeForward, nullptr));
             return true;
         }
     }
@@ -3710,6 +3710,29 @@
     }
 }
 
+namespace {
+
+// Defining this class type local to dispatchTouchEvents() and annotating
+// it with STACK_ALLOCATED(), runs into MSVC(VS 2013)'s C4822 warning
+// that the local class doesn't provide a local definition for 'operator new'.
+// Which it intentionally doesn't and shouldn't.
+//
+// Work around such toolchain bugginess by lifting out the type, thereby
+// taking it out of C4822's reach.
+class ChangedTouches final {
+    STACK_ALLOCATED();
+public:
+    // The touches corresponding to the particular change state this struct
+    // instance represents.
+    RefPtrWillBeMember<TouchList> m_touches;
+
+    using EventTargetSet = WillBeHeapHashSet<RefPtrWillBeMember<EventTarget>>;
+    // Set of targets involved in m_touches.
+    EventTargetSet m_targets;
+};
+
+} // namespace
+
 bool EventHandler::dispatchTouchEvents(const PlatformTouchEvent& event,
     WillBeHeapVector<TouchInfo>& touchInfos, bool freshTouchEvents, bool allTouchReleased)
 {
@@ -3727,14 +3750,7 @@
     TargetTouchesHeapMap touchesByTarget;
 
     // Array of touches per state, used to assemble the 'changedTouches' list.
-    using EventTargetSet = WillBeHeapHashSet<RefPtrWillBeMember<EventTarget>>;
-    struct {
-        // The touches corresponding to the particular change state this struct
-        // instance represents.
-        RefPtrWillBeMember<TouchList> m_touches;
-        // Set of targets involved in m_touches.
-        EventTargetSet m_targets;
-    } changedTouches[PlatformTouchPoint::TouchStateEnd];
+    ChangedTouches changedTouches[PlatformTouchPoint::TouchStateEnd];
 
     for (unsigned i = 0; i < touchInfos.size(); ++i) {
         const TouchInfo& touchInfo = touchInfos[i];
@@ -3798,8 +3814,7 @@
             continue;
 
         const AtomicString& eventName(touchEventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
-        const EventTargetSet& targetsForState = changedTouches[state].m_targets;
-        for (const RefPtrWillBeMember<EventTarget>& eventTarget : targetsForState) {
+        for (const auto& eventTarget : changedTouches[state].m_targets) {
             EventTarget* touchEventTarget = eventTarget.get();
             RefPtrWillBeRawPtr<TouchEvent> touchEvent = TouchEvent::create(
                 touches.get(), touchesByTarget.get(touchEventTarget), changedTouches[state].m_touches.get(),
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp
index 4c0a57a..2679a2bc 100644
--- a/third_party/WebKit/Source/core/page/FocusController.cpp
+++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -773,7 +773,7 @@
         frame->selection().setSelection(newSelection);
     }
 
-    element->focus(false, type, sourceCapabilities);
+    element->focus(FocusParams(SelectionBehaviorOnFocus::Reset, type, sourceCapabilities));
     return true;
 }
 
@@ -1026,7 +1026,7 @@
     Element* element = toElement(focusCandidate.focusableNode);
     ASSERT(element);
 
-    element->focus(false, type);
+    element->focus(FocusParams(SelectionBehaviorOnFocus::Reset, type, nullptr));
     return true;
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/animation/AnimationTimeline.js b/third_party/WebKit/Source/devtools/front_end/animation/AnimationTimeline.js
index 3e7bab7..0df465b2 100644
--- a/third_party/WebKit/Source/devtools/front_end/animation/AnimationTimeline.js
+++ b/third_party/WebKit/Source/devtools/front_end/animation/AnimationTimeline.js
@@ -261,6 +261,7 @@
             return;
         this._selectedGroup.seekTo(0);
         this._animateTime(0);
+        this._updateControlButton();
     },
 
     /**
@@ -393,8 +394,10 @@
             ui.element.classList.toggle("selected", this._selectedGroup === group);
         }
 
-        if (this._selectedGroup === group)
+        if (this._selectedGroup === group) {
+            this._replay();
             return;
+        }
         this._selectedGroup = group;
         this._previewMap.forEach(applySelectionClass, this);
         this._reset();
@@ -402,7 +405,7 @@
             this._addAnimation(anim);
         this.scheduleRedraw();
         this._timelineScrubber.classList.remove("hidden");
-        this._syncScrubber();
+        this._replay();
     },
 
     /**
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
index 9c8f7c946..8ab9ea34 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.cpp
@@ -31,6 +31,7 @@
 #include "core/dom/ExceptionCode.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/events/Event.h"
+#include "core/frame/UseCounter.h"
 #include "modules/mediastream/MediaStream.h"
 #include "modules/mediastream/MediaStreamTrackSourcesCallback.h"
 #include "modules/mediastream/MediaStreamTrackSourcesRequestImpl.h"
@@ -145,6 +146,7 @@
         exceptionState.throwDOMException(NotSupportedError, "No sources controller available; is this a detached window?");
         return;
     }
+    UseCounter::countDeprecation(context, UseCounter::MediaStreamTrackGetSources);
     MediaStreamTrackSourcesRequest* request = MediaStreamTrackSourcesRequestImpl::create(*context, callback);
     userMedia->requestSources(request);
 }
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
index 5c1f2e59..f9ac2563 100644
--- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
+++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
@@ -34,7 +34,7 @@
     readonly attribute boolean remote;
     readonly attribute DOMString readyState;
 
-    [CallWith=ExecutionContext, RaisesException, MeasureAs=MediaStreamTrackGetSources] static void getSources(MediaStreamTrackSourcesCallback callback);
+    [CallWith=ExecutionContext, RaisesException, DeprecateAs=MediaStreamTrackGetSources] static void getSources(MediaStreamTrackSourcesCallback callback);
     [ImplementedAs=stopTrack, RaisesException] void stop();
     [CallWith=ExecutionContext] MediaStreamTrack clone();
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
index 22c4148e..8230ce1 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.cpp
@@ -371,4 +371,9 @@
     handleMessageQueue();
 }
 
+bool PresentationConnection::isDisconnected() const
+{
+    return m_state == WebPresentationConnectionState::Closed || m_state == WebPresentationConnectionState::Terminated;
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
index 25c1096..7644a6e 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationConnection.h
@@ -116,6 +116,9 @@
     void didFinishLoadingBlob(PassRefPtr<DOMArrayBuffer>);
     void didFailLoadingBlob(FileError::ErrorCode);
 
+    // Returns true iff current state is closed or terminated.
+    bool isDisconnected() const;
+
     String m_id;
     String m_url;
     WebPresentationConnectionState m_state;
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index de86ef8..a710e85d 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -421,7 +421,7 @@
                 // focused, then the focus element shows with a focus ring but
                 // no caret and does respond to keyboard inputs.
                 if (element->isTextFormControl()) {
-                    element->updateFocusAppearance(true);
+                    element->updateFocusAppearance(SelectionBehaviorOnFocus::Restore);
                 } else if (element->isContentEditable()) {
                     // updateFocusAppearance() selects all the text of
                     // contentseditable DIVs. So we set the selection explicitly
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index 7e83339..bc80f45 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -2193,7 +2193,7 @@
                 // focused, then the focus element shows with a focus ring but
                 // no caret and does respond to keyboard inputs.
                 if (element->isTextFormControl()) {
-                    element->updateFocusAppearance(true);
+                    element->updateFocusAppearance(SelectionBehaviorOnFocus::Restore);
                 } else if (element->isContentEditable()) {
                     // updateFocusAppearance() selects all the text of
                     // contentseditable DIVs. So we set the selection explicitly
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 134179d..259a0161 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -60,8 +60,8 @@
     # This is just for completeness; any bot that uses this config should never actually run MB.
     'none': ['none'],
 
-    'noswarming_gn_release_bot_linux_dump_symbols': ['noswarming', 'gn', 'release_bot', 'linux_dump_symbols'],
     'noswarming_gyp_release_bot': ['noswarming', 'gyp', 'release_bot'],
+    'noswarming_gyp_release_bot_linux_dump_symbols': ['noswarming', 'gyp', 'release_bot', 'linux_dump_symbols'],
     'noswarming_gyp_release_bot_mac_strip': ['noswarming', 'gyp', 'release_bot', 'mac_strip'],
     'noswarming_gyp_release_bot_x86_linux_dump_symbols': ['noswarming', 'gyp', 'release_bot', 'x86', 'linux_dump_symbols'],
 
@@ -244,7 +244,7 @@
     'libfuzzer': { 'gn_args': 'use_libfuzzer=true' },
 
     'linux_dump_symbols': {
-      'gn_args': '', # TODO(GYP): Port linux_dump_symbols?
+      'gn_args': 'error', # TODO(GYP): Port linux_dump_symbols?
       'gyp_defines': 'linux_dump_symbols=1',
     },
 
@@ -353,7 +353,7 @@
     'chromium': {
       'Win': 'noswarming_gyp_release_bot',
       'Mac': 'noswarming_gyp_release_bot_mac_strip',
-      'Linux x64': 'noswarming_gn_release_bot_linux_dump_symbols',
+      'Linux x64': 'noswarming_gyp_release_bot_linux_dump_symbols',
       'Linux': 'noswarming_gyp_release_bot_x86_linux_dump_symbols',
       'Android': 'android_gyp_release_bot_minimal_symbols',
     },
@@ -492,7 +492,6 @@
       'linux_arm_tester': 'none',
       'linux_chromium_compile_dbg_32_ng': 'swarming_gyp_debug_trybot_x86',
       'linux_chromium_dbg_32_ng': 'swarming_gyp_debug_trybot_x86',
-      'linux_chromium_archive_rel_ng': 'noswarming_gn_release_bot_linux_dump_symbols',
       'linux_chromium_clobber_rel_ng': 'gyp_release_trybot',
       'linux_chromium_gn_upload': 'gn_linux_upload',
       'cast_shell_linux': 'cast_gyp_release_trybot',
diff --git a/tools/telemetry/telemetry/core/platform.py b/tools/telemetry/telemetry/core/platform.py
index ebd4223b..a163b252 100644
--- a/tools/telemetry/telemetry/core/platform.py
+++ b/tools/telemetry/telemetry/core/platform.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 import logging as real_logging
 import os
+import sys
 
 from telemetry.core import discover
 from telemetry.core import local_server
@@ -63,8 +64,9 @@
         return _remote_platforms[device.guid]
     return None
   except Exception:
+    current_exception = sys.exc_info()
     logging.error('Fail to create platform instance for %s.', device.name)
-    raise
+    raise current_exception[0], current_exception[1], current_exception[2]
 
 
 class Platform(object):
diff --git a/tools/telemetry/telemetry/timeline/thread.py b/tools/telemetry/telemetry/timeline/thread.py
index f04f9da..e58e6b8 100644
--- a/tools/telemetry/telemetry/timeline/thread.py
+++ b/tools/telemetry/telemetry/timeline/thread.py
@@ -158,6 +158,14 @@
     self.PushSlice(new_slice)
     return new_slice
 
+  def PushMarkSlice(self, category, name, timestamp, thread_timestamp,
+        args=None):
+    new_slice = slice_module.Slice(self, category, name, timestamp,
+                                   thread_timestamp=thread_timestamp,
+                                   args=args)
+    self.PushSlice(new_slice)
+    return new_slice
+
   def PushSlice(self, new_slice):
     self._newly_added_slices.append(new_slice)
     return new_slice
diff --git a/tools/telemetry/telemetry/timeline/trace_event_importer.py b/tools/telemetry/telemetry/timeline/trace_event_importer.py
index 6d79d410..111e241 100644
--- a/tools/telemetry/telemetry/timeline/trace_event_importer.py
+++ b/tools/telemetry/telemetry/timeline/trace_event_importer.py
@@ -140,6 +140,16 @@
         event['tdur'] / 1000.0 if 'tdur' in event else None,
         event['args'])
 
+  def _ProcessMarkEvent(self, event):
+    thread = (self._GetOrCreateProcess(event['pid'])
+        .GetOrCreateThread(event['tid']))
+    thread.PushMarkSlice(
+        event['cat'],
+        event['name'],
+        event['ts'] / 1000.0,
+        event['tts'] / 1000.0 if 'tts' in event else None,
+        event['args'] if 'args' in event else None)
+
   def _ProcessMetadataEvent(self, event):
     if event['name'] == 'thread_name':
       thread = (self._GetOrCreateProcess(event['pid'])
@@ -222,6 +232,8 @@
         self._ProcessFlowEvent(event)
       elif phase == 'v':
         self._ProcessMemoryDumpEvent(event)
+      elif phase == 'R':
+        self._ProcessMarkEvent(event)
       else:
         self._model.import_errors.append('Unrecognized event phase: ' +
             phase + '(' + event['name'] + ')')
diff --git a/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py b/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
index 10fd01f..ae4410d 100644
--- a/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
+++ b/tools/telemetry/telemetry/timeline/trace_event_importer_unittest.py
@@ -940,6 +940,39 @@
     self.assertTrue(slice_event.did_not_finish)
     self.assertEqual(0, len(slice_event.sub_slices))
 
+  def testImportMarkEvent(self):
+    events = [
+      {'name': 'a', 'pid': 52, 'ts': 629, 'cat': 'baz', 'tid': 53, 'ph': 'R'},
+      {'name': 'b', 'pid': 52, 'ts': 730, 'cat': 'foo', 'tid': 53, 'ph': 'R'},
+      {'name': 'c', 'pid': 52, 'ts': 740, 'cat': 'baz', 'tid': 53, 'ph': 'R'},
+    ]
+    trace_data = trace_data_module.TraceData(events)
+    m = timeline_model.TimelineModel(trace_data)
+    p = m.GetAllProcesses()[0]
+    t = p.threads[53]
+    self.assertEqual(3, len(t.all_slices))
+
+    slice_event = t.all_slices[0]
+    self.assertEqual('a', slice_event.name)
+    self.assertEqual('baz', slice_event.category)
+    self.assertAlmostEqual(0.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[1]
+    self.assertEqual('b', slice_event.name)
+    self.assertEqual('foo', slice_event.category)
+    self.assertAlmostEqual((730 - 629) / 1000.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
+    slice_event = t.all_slices[2]
+    self.assertEqual('c', slice_event.name)
+    self.assertEqual('baz', slice_event.category)
+    self.assertAlmostEqual((740 - 629) / 1000.0, slice_event.start)
+    self.assertFalse(slice_event.did_not_finish)
+    self.assertEqual(0, len(slice_event.sub_slices))
+
   def testImportFlowEvent(self):
     events = [
       {'name': 'a', 'cat': 'foo', 'id': 72, 'pid': 52, 'tid': 53, 'ts': 548,
diff --git a/tools/win/new_analyze_warnings/OWNERS b/tools/win/OWNERS
similarity index 100%
rename from tools/win/new_analyze_warnings/OWNERS
rename to tools/win/OWNERS
diff --git a/ui/aura/window_tree_host_platform.h b/ui/aura/window_tree_host_platform.h
index 41870b9..18b8e4d 100644
--- a/ui/aura/window_tree_host_platform.h
+++ b/ui/aura/window_tree_host_platform.h
@@ -43,7 +43,6 @@
   void SetPlatformWindow(scoped_ptr<ui::PlatformWindow> window);
   ui::PlatformWindow* platform_window() { return window_.get(); }
 
- private:
   // ui::PlatformWindowDelegate:
   void OnBoundsChanged(const gfx::Rect& new_bounds) override;
   void OnDamageRect(const gfx::Rect& damaged_region) override;
@@ -57,6 +56,7 @@
   void OnAcceleratedWidgetDestroyed() override;
   void OnActivationChanged(bool active) override;
 
+ private:
   gfx::AcceleratedWidget widget_;
   scoped_ptr<ui::PlatformWindow> window_;
   gfx::NativeCursor current_cursor_;
diff --git a/ui/events/cocoa/events_mac.mm b/ui/events/cocoa/events_mac.mm
index bf0dd31..804ffbd5 100644
--- a/ui/events/cocoa/events_mac.mm
+++ b/ui/events/cocoa/events_mac.mm
@@ -134,6 +134,11 @@
   return 0;
 }
 
+PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event) {
+  return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE);
+}
+
 gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& event) {
   if ([event respondsToSelector:@selector(hasPreciseScrollingDeltas)] &&
       [event hasPreciseScrollingDeltas]) {
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 71f5e27..0e7d0f19 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -320,7 +320,7 @@
 MouseEvent::MouseEvent(const base::NativeEvent& native_event)
     : LocatedEvent(native_event),
       changed_button_flags_(GetChangedMouseButtonFlagsFromNative(native_event)),
-      pointer_details_(PointerDetails(EventPointerType::POINTER_TYPE_MOUSE)) {
+      pointer_details_(GetMousePointerDetailsFromNative(native_event)) {
   if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED)
     SetClickCount(GetRepeatCount(*this));
 }
diff --git a/ui/events/event_utils.cc b/ui/events/event_utils.cc
index 1b3d7a9..c2d2adb 100644
--- a/ui/events/event_utils.cc
+++ b/ui/events/event_utils.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "ui/events/event.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/screen.h"
 
diff --git a/ui/events/event_utils.h b/ui/events/event_utils.h
index 7488439..ab4779a 100644
--- a/ui/events/event_utils.h
+++ b/ui/events/event_utils.h
@@ -9,6 +9,7 @@
 #include "base/event_types.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
+#include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/display.h"
@@ -93,6 +94,10 @@
 EVENTS_EXPORT int GetChangedMouseButtonFlagsFromNative(
     const base::NativeEvent& native_event);
 
+// Returns the detailed pointer information for mouse events.
+EVENTS_EXPORT PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event);
+
 // Gets the mouse wheel offsets from a native event.
 EVENTS_EXPORT gfx::Vector2d GetMouseWheelOffset(
     const base::NativeEvent& native_event);
diff --git a/ui/events/events_default.cc b/ui/events/events_default.cc
index 4fb85a34..7d55e8e 100644
--- a/ui/events/events_default.cc
+++ b/ui/events/events_default.cc
@@ -46,6 +46,14 @@
   return event->changed_button_flags();
 }
 
+PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event) {
+  const ui::MouseEvent* event =
+      static_cast<const ui::MouseEvent*>(native_event);
+  DCHECK(event->IsMouseEvent() || event->IsScrollEvent());
+  return event->pointer_details();
+}
+
 KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) {
   const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event);
   DCHECK(event->IsKeyEvent());
diff --git a/ui/events/events_stub.cc b/ui/events/events_stub.cc
index d4ba557..87c71a5 100644
--- a/ui/events/events_stub.cc
+++ b/ui/events/events_stub.cc
@@ -56,6 +56,11 @@
   return 0;
 }
 
+PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event) {
+  return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE);
+}
+
 gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) {
   NOTIMPLEMENTED();
   return gfx::Vector2d();
diff --git a/ui/events/win/events_win.cc b/ui/events/win/events_win.cc
index c2f35f43..18ddfa98 100644
--- a/ui/events/win/events_win.cc
+++ b/ui/events/win/events_win.cc
@@ -283,6 +283,11 @@
   return 0;
 }
 
+PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event) {
+  return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE);
+}
+
 gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) {
   DCHECK(native_event.message == WM_MOUSEWHEEL ||
          native_event.message == WM_MOUSEHWHEEL);
diff --git a/ui/events/x/events_x.cc b/ui/events/x/events_x.cc
index 6afb53c..cbc14b66 100644
--- a/ui/events/x/events_x.cc
+++ b/ui/events/x/events_x.cc
@@ -675,6 +675,11 @@
   return 0;
 }
 
+PointerDetails GetMousePointerDetailsFromNative(
+    const base::NativeEvent& native_event) {
+  return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE);
+}
+
 gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) {
   float x_offset, y_offset;
   if (GetScrollOffsets(
diff --git a/ui/gl/gl_image_ozone_native_pixmap.cc b/ui/gl/gl_image_ozone_native_pixmap.cc
index 818426178..9bcefcb 100644
--- a/ui/gl/gl_image_ozone_native_pixmap.cc
+++ b/ui/gl/gl_image_ozone_native_pixmap.cc
@@ -86,9 +86,9 @@
 GLImageOzoneNativePixmap::~GLImageOzoneNativePixmap() {
 }
 
-bool GLImageOzoneNativePixmap::Initialize(ui::NativePixmap* pixmap,
-                                          BufferFormat format) {
+bool GLImageOzoneNativePixmap::Initialize(ui::NativePixmap* pixmap) {
   DCHECK(!pixmap_);
+  BufferFormat format = pixmap->GetBufferFormat();
   if (pixmap->GetEGLClientBuffer()) {
     EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
     if (!gl::GLImageEGL::Initialize(EGL_NATIVE_PIXMAP_KHR,
diff --git a/ui/gl/gl_image_ozone_native_pixmap.h b/ui/gl/gl_image_ozone_native_pixmap.h
index f76e98b..d0e1cde 100644
--- a/ui/gl/gl_image_ozone_native_pixmap.h
+++ b/ui/gl/gl_image_ozone_native_pixmap.h
@@ -15,7 +15,7 @@
  public:
   GLImageOzoneNativePixmap(const Size& size, unsigned internalformat);
 
-  bool Initialize(ui::NativePixmap* pixmap, BufferFormat format);
+  bool Initialize(ui::NativePixmap* pixmap);
 
   // Overridden from GLImage:
   unsigned GetInternalFormat() override;
diff --git a/ui/gl/gl_surface_ozone.cc b/ui/gl/gl_surface_ozone.cc
index c4b7642fa..978c98231 100644
--- a/ui/gl/gl_surface_ozone.cc
+++ b/ui/gl/gl_surface_ozone.cc
@@ -575,7 +575,7 @@
       return false;
     scoped_refptr<GLImageOzoneNativePixmap> image =
         new GLImageOzoneNativePixmap(GetSize(), GL_BGRA_EXT);
-    if (!image->Initialize(pixmap.get(), gfx::BufferFormat::BGRA_8888))
+    if (!image->Initialize(pixmap.get()))
       return false;
     images_[i] = image;
     // Bind image to texture.
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc
index 60575d6..81aaf06 100644
--- a/ui/ozone/demo/surfaceless_gl_renderer.cc
+++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -43,7 +43,7 @@
                                gfx::BufferUsage::SCANOUT);
   scoped_refptr<gfx::GLImageOzoneNativePixmap> image(
       new gfx::GLImageOzoneNativePixmap(size, GL_RGB));
-  if (!image->Initialize(pixmap.get(), gfx::BufferFormat::BGRX_8888)) {
+  if (!image->Initialize(pixmap.get())) {
     LOG(ERROR) << "Failed to create GLImage";
     return false;
   }
diff --git a/ui/ozone/platform/cast/surface_factory_cast.cc b/ui/ozone/platform/cast/surface_factory_cast.cc
index f374f0de..174dd78 100644
--- a/ui/ozone/platform/cast/surface_factory_cast.cc
+++ b/ui/ozone/platform/cast/surface_factory_cast.cc
@@ -190,6 +190,9 @@
     }
     int GetDmaBufFd() override { return 0; }
     int GetDmaBufPitch() override { return 0; }
+    gfx::BufferFormat GetBufferFormat() override {
+      return gfx::BufferFormat::LAST;
+    }
     bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                               int plane_z_order,
                               gfx::OverlayTransform plane_transform,
@@ -197,8 +200,11 @@
                               const gfx::RectF& crop_rect) override {
       return true;
     }
-    void SetScalingCallback(const ScalingCallback& scaling_callback) override {}
-    scoped_refptr<NativePixmap> GetScaledPixmap(gfx::Size new_size) override {
+    void SetProcessingCallback(
+        const ProcessingCallback& processing_callback) override {}
+    scoped_refptr<NativePixmap> GetProcessedPixmap(
+        gfx::Size target_size,
+        gfx::BufferFormat target_format) override {
       return nullptr;
     }
     gfx::NativePixmapHandle ExportHandle() override {
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index eb98ea0..00d4333 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -277,10 +277,25 @@
       return DRM_FORMAT_ARGB8888;
     case gfx::BufferFormat::BGRX_8888:
       return DRM_FORMAT_XRGB8888;
+    case gfx::BufferFormat::UYVY_422:
+      return DRM_FORMAT_UYVY;
     default:
       NOTREACHED();
       return 0;
   }
 }
 
+gfx::BufferFormat GetBufferFormatFromFourCCFormat(int format) {
+  switch (format) {
+    case DRM_FORMAT_ARGB8888:
+      return gfx::BufferFormat::BGRA_8888;
+    case DRM_FORMAT_XRGB8888:
+      return gfx::BufferFormat::BGRX_8888;
+    case DRM_FORMAT_UYVY:
+      return gfx::BufferFormat::UYVY_422;
+    default:
+      NOTREACHED();
+      return gfx::BufferFormat::LAST;
+  }
+}
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index ca13fa4e..4630957 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -59,6 +59,7 @@
     const gfx::Point& origin);
 
 int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format);
+gfx::BufferFormat GetBufferFormatFromFourCCFormat(int format);
 
 }  // namespace ui
 
diff --git a/ui/ozone/platform/drm/gpu/drm_buffer.cc b/ui/ozone/platform/drm/gpu/drm_buffer.cc
index 4a2db96b7..bd2e9a9 100644
--- a/ui/ozone/platform/drm/gpu/drm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/drm_buffer.cc
@@ -13,25 +13,6 @@
 
 namespace {
 
-uint8_t GetColorDepth(SkColorType type) {
-  switch (type) {
-    case kUnknown_SkColorType:
-    case kAlpha_8_SkColorType:
-      return 0;
-    case kIndex_8_SkColorType:
-      return 8;
-    case kRGB_565_SkColorType:
-      return 16;
-    case kARGB_4444_SkColorType:
-      return 16;
-    case kN32_SkColorType:
-      return 32;
-    default:
-      NOTREACHED();
-      return 0;
-  }
-}
-
 uint32_t GetFourCCCodeForSkColorType(SkColorType type) {
   switch (type) {
     case kUnknown_SkColorType:
@@ -84,14 +65,17 @@
   }
 
   if (should_register_framebuffer) {
-    if (!drm_->AddFramebuffer(
-            info.width(), info.height(), GetColorDepth(info.colorType()),
-            info.bytesPerPixel() << 3, stride_, handle_, &framebuffer_)) {
-      PLOG(ERROR) << "DrmBuffer: AddFramebuffer: handle " << handle_;
+    uint32_t handles[4] = {0};
+    handles[0] = handle_;
+    uint32_t strides[4] = {0};
+    strides[0] = stride_;
+    uint32_t offsets[4] = {0};
+    fb_pixel_format_ = GetFourCCCodeForSkColorType(info.colorType());
+    if (!drm_->AddFramebuffer2(info.width(), info.height(), fb_pixel_format_,
+                               handles, strides, offsets, &framebuffer_, 0)) {
+      PLOG(ERROR) << "DrmBuffer: AddFramebuffer2: handle " << handle_;
       return false;
     }
-
-    fb_pixel_format_ = GetFourCCCodeForSkColorType(info.colorType());
   }
 
   surface_ =
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc
index ad4979c4..62d606e8 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -299,17 +299,18 @@
       drmModeGetConnector(file_.GetPlatformFile(), connector_id));
 }
 
-bool DrmDevice::AddFramebuffer(uint32_t width,
-                               uint32_t height,
-                               uint8_t depth,
-                               uint8_t bpp,
-                               uint32_t stride,
-                               uint32_t handle,
-                               uint32_t* framebuffer) {
+bool DrmDevice::AddFramebuffer2(uint32_t width,
+                                uint32_t height,
+                                uint32_t format,
+                                uint32_t handles[4],
+                                uint32_t strides[4],
+                                uint32_t offsets[4],
+                                uint32_t* framebuffer,
+                                uint32_t flags) {
   DCHECK(file_.IsValid());
-  TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handle);
-  return !drmModeAddFB(file_.GetPlatformFile(), width, height, depth, bpp,
-                       stride, handle, framebuffer);
+  TRACE_EVENT1("drm", "DrmDevice::AddFramebuffer", "handle", handles[0]);
+  return !drmModeAddFB2(file_.GetPlatformFile(), width, height, format, handles,
+                        strides, offsets, framebuffer, flags);
 }
 
 bool DrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h
index cadc429..ec3054a 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.h
+++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -77,15 +77,16 @@
   // Returns the connector properties for |connector_id|.
   virtual ScopedDrmConnectorPtr GetConnector(uint32_t connector_id);
 
-  // Register a buffer with the CRTC. On successful registration, the CRTC will
-  // assign a framebuffer ID to |framebuffer|.
-  virtual bool AddFramebuffer(uint32_t width,
-                              uint32_t height,
-                              uint8_t depth,
-                              uint8_t bpp,
-                              uint32_t stride,
-                              uint32_t handle,
-                              uint32_t* framebuffer);
+  // Register any format buffer with the CRTC. On successful registration, the
+  // CRTC will assign a framebuffer ID to |framebuffer|.
+  virtual bool AddFramebuffer2(uint32_t width,
+                               uint32_t height,
+                               uint32_t format,
+                               uint32_t handles[4],
+                               uint32_t strides[4],
+                               uint32_t offsets[4],
+                               uint32_t* framebuffer,
+                               uint32_t flags);
 
   // Deregister the given |framebuffer|.
   virtual bool RemoveFramebuffer(uint32_t framebuffer);
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
index 8c3c396..2e269bf 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.cc
@@ -20,6 +20,11 @@
 #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h"
 #include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h"
 
+namespace {
+// Optimal format for rendering on overlay.
+const gfx::BufferFormat kOverlayRenderFormat = gfx::BufferFormat::UYVY_422;
+}  // namespace
+
 namespace ui {
 
 GbmBuffer::GbmBuffer(const scoped_refptr<GbmDevice>& gbm,
@@ -80,12 +85,15 @@
   return true;
 }
 
-void GbmPixmap::SetScalingCallback(const ScalingCallback& scaling_callback) {
-  scaling_callback_ = scaling_callback;
+void GbmPixmap::SetProcessingCallback(
+    const ProcessingCallback& processing_callback) {
+  processing_callback_ = processing_callback;
 }
 
-scoped_refptr<NativePixmap> GbmPixmap::GetScaledPixmap(gfx::Size new_size) {
-  return scaling_callback_.Run(new_size);
+scoped_refptr<NativePixmap> GbmPixmap::GetProcessedPixmap(
+    gfx::Size target_size,
+    gfx::BufferFormat target_format) {
+  return processing_callback_.Run(target_size, target_format);
 }
 
 gfx::NativePixmapHandle GbmPixmap::ExportHandle() {
@@ -117,17 +125,23 @@
   return dma_buf_pitch_;
 }
 
+gfx::BufferFormat GbmPixmap::GetBufferFormat() {
+  return GetBufferFormatFromFourCCFormat(buffer_->GetFramebufferPixelFormat());
+}
+
 bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                                      int plane_z_order,
                                      gfx::OverlayTransform plane_transform,
                                      const gfx::Rect& display_bounds,
                                      const gfx::RectF& crop_rect) {
-  gfx::Size required_size;
-  if (plane_z_order &&
-      ShouldApplyScaling(display_bounds, crop_rect, &required_size)) {
-    scoped_refptr<NativePixmap> scaled_pixmap = GetScaledPixmap(required_size);
-    if (scaled_pixmap) {
-      return scaled_pixmap->ScheduleOverlayPlane(
+  gfx::Size target_size;
+  gfx::BufferFormat target_format;
+  if (plane_z_order && ShouldApplyProcessing(display_bounds, crop_rect,
+                                             &target_size, &target_format)) {
+    scoped_refptr<NativePixmap> processed_pixmap =
+        GetProcessedPixmap(target_size, target_format);
+    if (processed_pixmap) {
+      return processed_pixmap->ScheduleOverlayPlane(
           widget, plane_z_order, plane_transform, display_bounds, crop_rect);
     } else {
       return false;
@@ -146,25 +160,30 @@
   return true;
 }
 
-bool GbmPixmap::ShouldApplyScaling(const gfx::Rect& display_bounds,
-                                   const gfx::RectF& crop_rect,
-                                   gfx::Size* required_size) {
+bool GbmPixmap::ShouldApplyProcessing(const gfx::Rect& display_bounds,
+                                      const gfx::RectF& crop_rect,
+                                      gfx::Size* target_size,
+                                      gfx::BufferFormat* target_format) {
   if (crop_rect.width() == 0 || crop_rect.height() == 0) {
-    PLOG(ERROR) << "ShouldApplyScaling passed zero scaling target.";
+    PLOG(ERROR) << "ShouldApplyProcessing passed zero processing target.";
     return false;
   }
 
   if (!buffer_) {
-    PLOG(ERROR) << "ShouldApplyScaling requires a buffer.";
+    PLOG(ERROR) << "ShouldApplyProcessing requires a buffer.";
     return false;
   }
 
+  // TODO(william.xie): Figure out the optimal render format for overlay.
+  // See http://crbug.com/553264.
+  *target_format = kOverlayRenderFormat;
   gfx::Size pixmap_size = buffer_->GetSize();
   // If the required size is not integer-sized, round it to the next integer.
-  *required_size = gfx::ToCeiledSize(
+  *target_size = gfx::ToCeiledSize(
       gfx::SizeF(display_bounds.width() / crop_rect.width(),
                  display_bounds.height() / crop_rect.height()));
-  return pixmap_size != *required_size;
+
+  return pixmap_size != *target_size || GetBufferFormat() != *target_format;
 }
 
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer.h b/ui/ozone/platform/drm/gpu/gbm_buffer.h
index a8edaad0..d63963d8 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer.h
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer.h
@@ -44,13 +44,17 @@
   explicit GbmPixmap(GbmSurfaceFactory* surface_manager);
   void Initialize(base::ScopedFD dma_buf, int dma_buf_pitch);
   bool InitializeFromBuffer(const scoped_refptr<GbmBuffer>& buffer);
-  void SetScalingCallback(const ScalingCallback& scaling_callback) override;
-  scoped_refptr<NativePixmap> GetScaledPixmap(gfx::Size new_size) override;
+  void SetProcessingCallback(
+      const ProcessingCallback& processing_callback) override;
+  scoped_refptr<NativePixmap> GetProcessedPixmap(
+      gfx::Size target_size,
+      gfx::BufferFormat target_format) override;
 
   // NativePixmap:
   void* GetEGLClientBuffer() override;
   int GetDmaBufFd() override;
   int GetDmaBufPitch() override;
+  gfx::BufferFormat GetBufferFormat() override;
   bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                             int plane_z_order,
                             gfx::OverlayTransform plane_transform,
@@ -62,9 +66,10 @@
 
  private:
   ~GbmPixmap() override;
-  bool ShouldApplyScaling(const gfx::Rect& display_bounds,
-                          const gfx::RectF& crop_rect,
-                          gfx::Size* required_size);
+  bool ShouldApplyProcessing(const gfx::Rect& display_bounds,
+                             const gfx::RectF& crop_rect,
+                             gfx::Size* target_size,
+                             gfx::BufferFormat* target_format);
 
   scoped_refptr<GbmBuffer> buffer_;
   base::ScopedFD dma_buf_;
@@ -72,7 +77,7 @@
 
   GbmSurfaceFactory* surface_manager_;
 
-  ScalingCallback scaling_callback_;
+  ProcessingCallback processing_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(GbmPixmap);
 };
diff --git a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
index 355b0af..5f27c51 100644
--- a/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_buffer_base.cc
@@ -11,35 +11,31 @@
 
 namespace ui {
 
-namespace {
-
-// Pixel configuration for the current buffer format.
-// TODO(dnicoara) These will need to change once we query the hardware for
-// supported configurations.
-const uint8_t kColorDepth = 24;
-const uint8_t kPixelDepth = 32;
-
-}  // namespace
-
 GbmBufferBase::GbmBufferBase(const scoped_refptr<DrmDevice>& drm,
                              gbm_bo* bo,
                              bool scanout)
     : drm_(drm), bo_(bo) {
   if (scanout) {
-    if (!drm_->AddFramebuffer(gbm_bo_get_width(bo), gbm_bo_get_height(bo),
-                              kColorDepth, kPixelDepth, gbm_bo_get_stride(bo),
-                              gbm_bo_get_handle(bo).u32, &framebuffer_)) {
-      PLOG(ERROR) << "Failed to register buffer";
-      return;
-    }
-
     fb_pixel_format_ = gbm_bo_get_format(bo);
     if (fb_pixel_format_ == GBM_FORMAT_ARGB8888)
       fb_pixel_format_ = GBM_FORMAT_XRGB8888;
 
-    // For now, we always create a frame buffer of 24 bit color depth and
-    // support only XRGB format.
-    DCHECK(fb_pixel_format_ == GBM_FORMAT_XRGB8888);
+    // For now, we only support XRGB and UYVY format.
+    DCHECK(fb_pixel_format_ == GBM_FORMAT_XRGB8888 ||
+           fb_pixel_format_ == GBM_FORMAT_UYVY);
+
+    uint32_t handles[4] = {0};
+    handles[0] = gbm_bo_get_handle(bo).u32;
+    uint32_t strides[4] = {0};
+    strides[0] = gbm_bo_get_stride(bo);
+    uint32_t offsets[4] = {0};
+
+    if (!drm_->AddFramebuffer2(gbm_bo_get_width(bo), gbm_bo_get_height(bo),
+                               fb_pixel_format_, handles, strides, offsets,
+                               &framebuffer_, 0)) {
+      PLOG(ERROR) << "Failed to register buffer";
+      return;
+    }
   }
 }
 
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
index 53f5773..8f3e751 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -119,13 +119,14 @@
   return ScopedDrmConnectorPtr(DrmAllocator<drmModeConnector>());
 }
 
-bool MockDrmDevice::AddFramebuffer(uint32_t width,
-                                   uint32_t height,
-                                   uint8_t depth,
-                                   uint8_t bpp,
-                                   uint32_t stride,
-                                   uint32_t handle,
-                                   uint32_t* framebuffer) {
+bool MockDrmDevice::AddFramebuffer2(uint32_t width,
+                                    uint32_t height,
+                                    uint32_t format,
+                                    uint32_t handles[4],
+                                    uint32_t strides[4],
+                                    uint32_t offsets[4],
+                                    uint32_t* framebuffer,
+                                    uint32_t flags) {
   add_framebuffer_call_count_++;
   *framebuffer = add_framebuffer_call_count_;
   return add_framebuffer_expectation_;
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h
index e14c7fc..c492606 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -70,13 +70,14 @@
   bool SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) override;
   bool DisableCrtc(uint32_t crtc_id) override;
   ScopedDrmConnectorPtr GetConnector(uint32_t connector_id) override;
-  bool AddFramebuffer(uint32_t width,
-                      uint32_t height,
-                      uint8_t depth,
-                      uint8_t bpp,
-                      uint32_t stride,
-                      uint32_t handle,
-                      uint32_t* framebuffer) override;
+  bool AddFramebuffer2(uint32_t width,
+                       uint32_t height,
+                       uint32_t format,
+                       uint32_t handles[4],
+                       uint32_t strides[4],
+                       uint32_t offsets[4],
+                       uint32_t* framebuffer,
+                       uint32_t flags) override;
   bool RemoveFramebuffer(uint32_t framebuffer) override;
   ScopedDrmFramebufferPtr GetFramebuffer(uint32_t framebuffer) override;
   bool PageFlip(uint32_t crtc_id,
diff --git a/ui/ozone/public/native_pixmap.h b/ui/ozone/public/native_pixmap.h
index 5bb007a..a472fb5 100644
--- a/ui/ozone/public/native_pixmap.h
+++ b/ui/ozone/public/native_pixmap.h
@@ -27,6 +27,7 @@
   virtual void* /* EGLClientBuffer */ GetEGLClientBuffer() = 0;
   virtual int GetDmaBufFd() = 0;
   virtual int GetDmaBufPitch() = 0;
+  virtual gfx::BufferFormat GetBufferFormat() = 0;
 
   // Sets the overlay plane to switch to at the next page flip.
   // |w| specifies the screen to display this overlay plane on.
@@ -45,14 +46,19 @@
                                     const gfx::Rect& display_bounds,
                                     const gfx::RectF& crop_rect) = 0;
 
-  // This represents a callback function pointing to scaling unit like VPP
-  // to do scaling operations on native pixmap with required size.
-  typedef base::Callback<scoped_refptr<NativePixmap>(gfx::Size)>
-      ScalingCallback;
+  // This represents a callback function pointing to processing unit like VPP to
+  // do post-processing operations on native pixmap with required size and
+  // format.
+  typedef base::Callback<scoped_refptr<NativePixmap>(gfx::Size,
+                                                     gfx::BufferFormat)>
+      ProcessingCallback;
 
-  // Set callback function for the pixmap used for scaling.
-  virtual void SetScalingCallback(const ScalingCallback& scaling_callback) = 0;
-  virtual scoped_refptr<NativePixmap> GetScaledPixmap(gfx::Size new_size) = 0;
+  // Set callback function for the pixmap used for post processing.
+  virtual void SetProcessingCallback(
+      const ProcessingCallback& processing_callback) = 0;
+  virtual scoped_refptr<NativePixmap> GetProcessedPixmap(
+      gfx::Size target_size,
+      gfx::BufferFormat target_format) = 0;
 
   // Export the buffer for sharing across processes.
   // Any file descriptors in the exported handle are owned by the caller.
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index 9c92efb..e65c390 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -14,8 +14,6 @@
     "input_method_mus.h",
     "native_widget_mus.cc",
     "native_widget_mus.h",
-    "native_widget_view_manager.cc",
-    "native_widget_view_manager.h",
     "surface_binding.cc",
     "surface_binding.h",
     "surface_context_factory.cc",
diff --git a/ui/views/mus/native_widget_mus.cc b/ui/views/mus/native_widget_mus.cc
index efed7c2..5fc56f3 100644
--- a/ui/views/mus/native_widget_mus.cc
+++ b/ui/views/mus/native_widget_mus.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/mus/native_widget_mus.h"
 
+#include "base/thread_task_runner_handle.h"
 #include "components/mus/public/cpp/property_type_converters.h"
 #include "components/mus/public/cpp/window.h"
 #include "mojo/converters/geometry/geometry_type_converters.h"
@@ -173,8 +174,16 @@
       native_widget_delegate_(delegate),
       surface_type_(surface_type),
       show_state_before_fullscreen_(ui::PLATFORM_WINDOW_STATE_UNKNOWN),
-      content_(new aura::Window(this)) {}
-NativeWidgetMus::~NativeWidgetMus() {}
+      ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
+      content_(new aura::Window(this)),
+      close_widget_factory_(this) {}
+
+NativeWidgetMus::~NativeWidgetMus() {
+  if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+    delete native_widget_delegate_;
+  else
+    CloseNow();
+}
 
 // static
 void NativeWidgetMus::SetWindowManagerClientAreaInsets(
@@ -185,6 +194,25 @@
   window_manager_client_area_insets = new WindowManagerClientAreaInsets(insets);
 }
 
+void NativeWidgetMus::OnPlatformWindowClosed() {
+  GetWidget()->Close();
+}
+
+void NativeWidgetMus::OnActivationChanged(bool active) {
+  if (!native_widget_delegate_)
+    return;
+  if (active) {
+    native_widget_delegate_->OnNativeFocus();
+    GetWidget()->GetFocusManager()->RestoreFocusedView();
+  } else {
+    native_widget_delegate_->OnNativeBlur();
+    GetWidget()->GetFocusManager()->StoreFocusedView(true);
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetMus, private:
+
 // static
 void NativeWidgetMus::ConfigurePropertiesForNewWindow(
     const Widget::InitParams& init_params,
@@ -205,8 +233,9 @@
 }
 
 void NativeWidgetMus::InitNativeWidget(const Widget::InitParams& params) {
+  ownership_ = params.ownership;
   window_tree_host_.reset(
-      new WindowTreeHostMus(shell_, window_, surface_type_));
+      new WindowTreeHostMus(shell_, this, window_, surface_type_));
   window_tree_host_->InitHost();
 
   focus_client_.reset(new wm::FocusController(new FocusRulesImpl));
@@ -231,6 +260,8 @@
 
   window_tree_host_->window()->AddChild(content_);
   // TODO(beng): much else, see [Desktop]NativeWidgetAura.
+
+  native_widget_delegate_->OnNativeWidgetCreated(false);
 }
 
 bool NativeWidgetMus::ShouldUseNativeFrame() const {
@@ -383,11 +414,18 @@
 }
 
 void NativeWidgetMus::Close() {
-  // NOTIMPLEMENTED();
+  Hide();
+  if (!close_widget_factory_.HasWeakPtrs()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(&NativeWidgetMus::CloseNow,
+                              close_widget_factory_.GetWeakPtr()));
+  }
 }
 
 void NativeWidgetMus::CloseNow() {
-  // NOTIMPLEMENTED();
+  // Note: Deleting |content_| triggers the |OnWindowDestroyed()| callback,
+  // which can delete |this|.
+  delete content_;
 }
 
 void NativeWidgetMus::Show() {
@@ -411,7 +449,7 @@
 
 bool NativeWidgetMus::IsVisible() const {
   // TODO(beng): this should probably be wired thru PlatformWindow.
-  return window_tree_host_->mus_window()->visible();
+  return window_->visible();
 }
 
 void NativeWidgetMus::Activate() {
@@ -638,13 +676,12 @@
 }
 
 void NativeWidgetMus::OnWindowDestroying(aura::Window* window) {
-  // Cleanup happens in OnHostClosed().
 }
 
 void NativeWidgetMus::OnWindowDestroyed(aura::Window* window) {
-  // Cleanup happens in OnHostClosed(). We own |content_window_| (indirectly by
-  // way of |dispatcher_|) so there should be no need to do any processing
-  // here.
+  native_widget_delegate_->OnNativeWidgetDestroyed();
+  if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+    delete this;
 }
 
 void NativeWidgetMus::OnWindowTargetVisibilityChanged(bool visible) {
diff --git a/ui/views/mus/native_widget_mus.h b/ui/views/mus/native_widget_mus.h
index 504d4c5..81e9624 100644
--- a/ui/views/mus/native_widget_mus.h
+++ b/ui/views/mus/native_widget_mus.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "components/mus/public/interfaces/window_manager.mojom.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/platform_window/platform_window_delegate.h"
@@ -70,6 +71,9 @@
 
   mus::Window* window() { return window_; }
 
+  void OnPlatformWindowClosed();
+  void OnActivationChanged(bool active);
+
  protected:
   // internal::NativeWidgetPrivate:
   NonClientFrameView* CreateNonClientFrameView() override;
@@ -195,12 +199,16 @@
   const mus::mojom::SurfaceType surface_type_;
   ui::PlatformWindowState show_state_before_fullscreen_;
 
+  // See class documentation for Widget in widget.h for a note about ownership.
+  Widget::InitParams::Ownership ownership_;
+
   // Aura configuration.
   scoped_ptr<WindowTreeHostMus> window_tree_host_;
   aura::Window* content_;
   scoped_ptr<wm::FocusController> focus_client_;
   scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
   scoped_ptr<aura::client::WindowTreeClient> window_tree_client_;
+  base::WeakPtrFactory<NativeWidgetMus> close_widget_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NativeWidgetMus);
 };
diff --git a/ui/views/mus/native_widget_view_manager.cc b/ui/views/mus/native_widget_view_manager.cc
deleted file mode 100644
index 826abad..0000000
--- a/ui/views/mus/native_widget_view_manager.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/mus/native_widget_view_manager.h"
-
-#include "components/mus/public/cpp/window.h"
-#include "components/mus/public/cpp/window_observer.h"
-#include "components/mus/public/interfaces/window_manager.mojom.h"
-#include "mojo/converters/geometry/geometry_type_converters.h"
-#include "ui/aura/client/default_capture_client.h"
-#include "ui/aura/layout_manager.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_event_dispatcher.h"
-#include "ui/base/ime/input_method_delegate.h"
-#include "ui/views/mus/input_method_mus.h"
-#include "ui/views/mus/window_tree_host_mus.h"
-#include "ui/wm/core/base_focus_rules.h"
-#include "ui/wm/core/capture_controller.h"
-#include "ui/wm/core/focus_controller.h"
-
-namespace views {
-namespace {
-
-// TODO: figure out what this should be.
-class FocusRulesImpl : public wm::BaseFocusRules {
- public:
-  FocusRulesImpl() {}
-  ~FocusRulesImpl() override {}
-
-  bool SupportsChildActivation(aura::Window* window) const override {
-    return true;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FocusRulesImpl);
-};
-
-class NativeWidgetWindowObserver : public mus::WindowObserver {
- public:
-  NativeWidgetWindowObserver(NativeWidgetViewManager* view_manager)
-      : view_manager_(view_manager) {
-    view_manager_->window_->AddObserver(this);
-  }
-
-  ~NativeWidgetWindowObserver() override {
-    if (view_manager_->window_)
-      view_manager_->window_->RemoveObserver(this);
-  }
-
- private:
-  // WindowObserver:
-  void OnWindowDestroyed(mus::Window* view) override {
-    DCHECK_EQ(view, view_manager_->window_);
-    view->RemoveObserver(this);
-    view_manager_->window_ = nullptr;
-    // TODO(sky): WindowTreeHostMus assumes the View outlives it.
-    // NativeWidgetWindowObserver needs to deal, likely by deleting this.
-  }
-
-  NativeWidgetViewManager* const view_manager_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeWidgetWindowObserver);
-};
-
-class WindowTreeHostWindowLayoutManager : public aura::LayoutManager {
- public:
-  WindowTreeHostWindowLayoutManager(aura::Window* outer, aura::Window* inner)
-      : outer_(outer), inner_(inner) {}
-  ~WindowTreeHostWindowLayoutManager() override {}
-
- private:
-  // aura::LayoutManager:
-  void OnWindowResized() override { inner_->SetBounds(outer_->bounds()); }
-  void OnWindowAddedToLayout(aura::Window* child) override {}
-  void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
-  void OnWindowRemovedFromLayout(aura::Window* child) override {}
-  void OnChildWindowVisibilityChanged(aura::Window* child,
-                                      bool visible) override {}
-  void SetChildBounds(aura::Window* child,
-                      const gfx::Rect& requested_bounds) override {
-    SetChildBoundsDirect(child, requested_bounds);
-  }
-
-  aura::Window* outer_;
-  aura::Window* inner_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowTreeHostWindowLayoutManager);
-};
-
-}  // namespace
-
-NativeWidgetViewManager::NativeWidgetViewManager(
-    views::internal::NativeWidgetDelegate* delegate,
-    mojo::Shell* shell,
-    mus::Window* window)
-    : NativeWidgetAura(delegate), window_(window) {
-  window_tree_host_.reset(
-      new WindowTreeHostMus(shell, window_, mus::mojom::SURFACE_TYPE_DEFAULT));
-  window_tree_host_->InitHost();
-
-  focus_client_.reset(new wm::FocusController(new FocusRulesImpl));
-
-  aura::client::SetFocusClient(window_tree_host_->window(),
-                               focus_client_.get());
-  aura::client::SetActivationClient(window_tree_host_->window(),
-                                    focus_client_.get());
-  window_tree_host_->window()->AddPreTargetHandler(focus_client_.get());
-  window_tree_host_->window()->SetLayoutManager(
-      new WindowTreeHostWindowLayoutManager(window_tree_host_->window(),
-                                            GetNativeWindow()));
-
-  capture_client_.reset(
-      new aura::client::DefaultCaptureClient(window_tree_host_->window()));
-
-  window_observer_.reset(new NativeWidgetWindowObserver(this));
-}
-
-NativeWidgetViewManager::~NativeWidgetViewManager() {}
-
-void NativeWidgetViewManager::InitNativeWidget(
-    const views::Widget::InitParams& in_params) {
-  views::Widget::InitParams params(in_params);
-  params.parent = window_tree_host_->window();
-  NativeWidgetAura::InitNativeWidget(params);
-}
-
-void NativeWidgetViewManager::OnWindowVisibilityChanged(aura::Window* window,
-                                                        bool visible) {
-  window_->SetVisible(visible);
-  // NOTE: We could also update aura::Window's visibility when the View's
-  // visibility changes, but this code isn't going to be around for very long so
-  // I'm not bothering.
-}
-
-}  // namespace views
diff --git a/ui/views/mus/native_widget_view_manager.h b/ui/views/mus/native_widget_view_manager.h
deleted file mode 100644
index c52cf1b..0000000
--- a/ui/views/mus/native_widget_view_manager.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_MUS_NATIVE_WIDGET_VIEW_MANAGER_H_
-#define UI_VIEWS_MUS_NATIVE_WIDGET_VIEW_MANAGER_H_
-
-#include "ui/views/widget/native_widget_aura.h"
-
-namespace aura {
-namespace client {
-class DefaultCaptureClient;
-}
-}
-
-namespace mojo {
-class Shell;
-}
-
-namespace mus {
-class Window;
-namespace mojom {
-class WindowManager;
-}
-}
-
-namespace ui {
-namespace internal {
-class InputMethodDelegate;
-}
-}
-
-namespace wm {
-class FocusController;
-}
-
-namespace views {
-
-namespace {
-class NativeWidgetWindowObserver;
-}
-
-class WindowTreeHostMus;
-
-class NativeWidgetViewManager : public views::NativeWidgetAura {
- public:
-  NativeWidgetViewManager(views::internal::NativeWidgetDelegate* delegate,
-                          mojo::Shell* shell,
-                          mus::Window* window);
-  ~NativeWidgetViewManager() override;
-
- private:
-  friend class NativeWidgetWindowObserver;
-
-  // Overridden from internal::NativeWidgetAura:
-  void InitNativeWidget(const views::Widget::InitParams& in_params) override;
-  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
-
-  scoped_ptr<WindowTreeHostMus> window_tree_host_;
-  scoped_ptr<NativeWidgetWindowObserver> window_observer_;
-
-  scoped_ptr<wm::FocusController> focus_client_;
-
-  mus::Window* window_;
-
-  scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
-
-  DISALLOW_COPY_AND_ASSIGN(NativeWidgetViewManager);
-};
-
-}  // namespace views
-
-#endif  // UI_VIEWS_MUS_NATIVE_WIDGET_VIEW_MANAGER_H_
diff --git a/ui/views/mus/window_tree_host_mus.cc b/ui/views/mus/window_tree_host_mus.cc
index a2efd52..61c6f46 100644
--- a/ui/views/mus/window_tree_host_mus.cc
+++ b/ui/views/mus/window_tree_host_mus.cc
@@ -20,6 +20,7 @@
 #include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 #include "ui/views/mus/input_method_mus.h"
+#include "ui/views/mus/native_widget_mus.h"
 #include "ui/views/mus/surface_context_factory.h"
 #include "ui/views/mus/window_manager_connection.h"
 
@@ -45,8 +46,10 @@
   }
 
   ~PlatformWindowMus() override {
-    if (mus_window_)
-      mus_window_->RemoveObserver(this);
+    if (!mus_window_)
+      return;
+    mus_window_->RemoveObserver(this);
+    mus_window_->Destroy();
   }
 
  private:
@@ -89,8 +92,8 @@
   // mus::WindowObserver:
   void OnWindowDestroyed(mus::Window* window) override {
     DCHECK_EQ(mus_window_, window);
-    mus_window_ = nullptr;
     delegate_->OnClosed();
+    mus_window_ = nullptr;
   }
 
   void OnWindowBoundsChanged(mus::Window* window,
@@ -159,24 +162,25 @@
 // WindowTreeHostMus, public:
 
 WindowTreeHostMus::WindowTreeHostMus(mojo::Shell* shell,
+                                     NativeWidgetMus* native_widget,
                                      mus::Window* window,
                                      mus::mojom::SurfaceType surface_type)
-    : mus_window_(window),
+    : native_widget_(native_widget),
       show_state_(ui::PLATFORM_WINDOW_STATE_UNKNOWN) {
   context_factory_.reset(
-      new SurfaceContextFactory(shell, mus_window_, surface_type));
+      new SurfaceContextFactory(shell, window, surface_type));
   // WindowTreeHost creates the compositor using the ContextFactory from
   // aura::Env. Install |context_factory_| there so that |context_factory_| is
   // picked up.
   ui::ContextFactory* default_context_factory =
       aura::Env::GetInstance()->context_factory();
   aura::Env::GetInstance()->set_context_factory(context_factory_.get());
-  SetPlatformWindow(make_scoped_ptr(new PlatformWindowMus(this, mus_window_)));
+  SetPlatformWindow(make_scoped_ptr(new PlatformWindowMus(this, window)));
   compositor()->SetHostHasTransparentBackground(true);
   aura::Env::GetInstance()->set_context_factory(default_context_factory);
   DCHECK_EQ(context_factory_.get(), compositor()->context_factory());
 
-  input_method_.reset(new InputMethodMUS(this, mus_window_));
+  input_method_.reset(new InputMethodMUS(this, window));
   SetSharedInputMethod(input_method_.get());
 }
 
@@ -185,10 +189,18 @@
   DestroyDispatcher();
 }
 
+void WindowTreeHostMus::DispatchEvent(ui::Event* event) {
+  if (event->IsKeyEvent() && GetInputMethod()) {
+    GetInputMethod()->DispatchKeyEvent(static_cast<ui::KeyEvent*>(event));
+    event->StopPropagation();
+    return;
+  }
+  WindowTreeHostPlatform::DispatchEvent(event);
+}
+
 void WindowTreeHostMus::OnClosed() {
-  // TODO(sad): NativeWidgetMus needs to know about this, and tear down the
-  // associated Widget (and this WindowTreeHostMus too).
-  NOTIMPLEMENTED();
+  if (native_widget_)
+    native_widget_->OnPlatformWindowClosed();
 }
 
 void WindowTreeHostMus::OnWindowStateChanged(ui::PlatformWindowState state) {
@@ -200,6 +212,9 @@
     GetInputMethod()->OnFocus();
   else
     GetInputMethod()->OnBlur();
+  if (native_widget_)
+    native_widget_->OnActivationChanged(active);
+  WindowTreeHostPlatform::OnActivationChanged(active);
 }
 
 }  // namespace views
diff --git a/ui/views/mus/window_tree_host_mus.h b/ui/views/mus/window_tree_host_mus.h
index ecbce97a..758e97b 100644
--- a/ui/views/mus/window_tree_host_mus.h
+++ b/ui/views/mus/window_tree_host_mus.h
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "components/mus/public/interfaces/window_tree.mojom.h"
 #include "ui/aura/window_tree_host_platform.h"
+#include "ui/views/widget/native_widget_private.h"
 
 class SkBitmap;
 
@@ -26,27 +27,28 @@
 namespace views {
 
 class InputMethodMUS;
+class NativeWidgetMus;
 class SurfaceContextFactory;
 
 class WindowTreeHostMus : public aura::WindowTreeHostPlatform {
  public:
   WindowTreeHostMus(mojo::Shell* shell,
+                    NativeWidgetMus* native_widget_,
                     mus::Window* window,
                     mus::mojom::SurfaceType surface_type);
   ~WindowTreeHostMus() override;
 
-  mus::Window* mus_window() { return mus_window_; }
-
   using WindowTreeHostPlatform::platform_window;
   ui::PlatformWindowState show_state() const { return show_state_; }
 
  private:
   // aura::WindowTreeHostPlatform:
+  void DispatchEvent(ui::Event* event) override;
   void OnClosed() override;
   void OnWindowStateChanged(ui::PlatformWindowState new_state) override;
   void OnActivationChanged(bool active) override;
 
-  mus::Window* mus_window_;
+  NativeWidgetMus* native_widget_;
   scoped_ptr<InputMethodMUS> input_method_;
   scoped_ptr<SurfaceContextFactory> context_factory_;
   ui::PlatformWindowState show_state_;